From db53aabcf27fe7d9dccd566b7011f230a148d10b Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sat, 26 Mar 2011 15:50:28 -0400 Subject: [PATCH] You can now send global, mentions, or private (direct message) Got Tomahawk? tweets --- src/sip/twitter/twitter.cpp | 178 +++++++++++++----------- src/sip/twitter/twitter.h | 1 + src/sip/twitter/twitterconfigwidget.cpp | 111 +++++++++++++-- src/sip/twitter/twitterconfigwidget.h | 8 +- src/sip/twitter/twitterconfigwidget.ui | 164 +++++++++++++++++++++- 5 files changed, 362 insertions(+), 100 deletions(-) diff --git a/src/sip/twitter/twitter.cpp b/src/sip/twitter/twitter.cpp index c4e3e9bb5..0bd5c69e7 100644 --- a/src/sip/twitter/twitter.cpp +++ b/src/sip/twitter/twitter.cpp @@ -370,12 +370,57 @@ TwitterPlugin::friendsTimelineStatuses( const QList< QTweetStatus > &statuses ) QMetaObject::invokeMethod( this, "pollDirectMessages", Qt::AutoConnection ); } +void +TwitterPlugin::parseGotTomahawk( const QRegExp ®ex, const QString &screenName, const QString &text ) +{ + QString myScreenName = TomahawkSettings::instance()->twitterScreenName(); + qDebug() << "TwitterPlugin found an exact matching Got Tomahawk? mention or direct message from user " << screenName; + if ( text.startsWith( '@' ) && regex.captureCount() >= 2 && regex.cap( 1 ) != QString( '@' + myScreenName ) ) + { + qDebug() << "TwitterPlugin skipping mention because it's directed @someone that isn't us"; + return; + } + + QString node; + for ( int i = 0; i < regex.captureCount(); ++i ) + { + if ( regex.cap( i ) == QString( "Got Tomahawk?" ) ) + { + QString nodeCap = regex.cap( i + 1 ); + nodeCap.chop( 1 ); + node = nodeCap.mid( 1 ); + } + } + if ( node.isEmpty() ) + { + qDebug() << "TwitterPlugin could not parse node out of the tweet"; + return; + } + else + qDebug() << "TwitterPlugin parsed node " << node << " out of the tweet"; + + if ( screenName == myScreenName && node == Database::instance()->dbid() ) + { + qDebug() << "My screen name and my dbid found; ignoring"; + return; + } + + QHash< QString, QVariant > peerData; + if( m_cachedPeers.contains( screenName ) ) + { + peerData = m_cachedPeers[screenName].toHash(); + //force a re-send of info but no need to re-register + peerData["resend"] = QVariant::fromValue< bool >( true ); + } + peerData["node"] = QVariant::fromValue< QString >( node ); + QMetaObject::invokeMethod( this, "registerOffer", Q_ARG( QString, screenName ), QGenericArgument( "QHash< QString, QVariant >", (const void*)&peerData ) ); +} + void TwitterPlugin::mentionsStatuses( const QList< QTweetStatus > &statuses ) { qDebug() << Q_FUNC_INFO; QRegExp regex( QString( "^(@[a-zA-Z0-9]+ )?(Got Tomahawk\\?) (\\{[a-fA-F0-9\\-]+\\}) (.*)$" ), Qt::CaseSensitive, QRegExp::RegExp2 ); - QString myScreenName = TomahawkSettings::instance()->twitterScreenName(); QHash< QString, QTweetStatus > latestHash; foreach ( QTweetStatus status, statuses ) @@ -393,50 +438,9 @@ TwitterPlugin::mentionsStatuses( const QList< QTweetStatus > &statuses ) { if ( status.id() > m_cachedMentionsSinceId ) m_cachedMentionsSinceId = status.id(); - + if ( regex.exactMatch( status.text() ) ) - { - qDebug() << "TwitterPlugin found an exact matching mention from user " << status.user().screenName(); - if ( status.text().startsWith( '@' ) && regex.captureCount() >= 2 && regex.cap( 1 ) != QString( '@' + myScreenName ) ) - { - qDebug() << "TwitterPlugin skipping mention because it's directed @someone that isn't us"; - continue; - } - - QString node; - for ( int i = 0; i < regex.captureCount(); ++i ) - { - if ( regex.cap( i ) == QString( "Got Tomahawk?" ) ) - { - QString nodeCap = regex.cap( i + 1 ); - nodeCap.chop( 1 ); - node = nodeCap.mid( 1 ); - } - } - if ( node.isEmpty() ) - { - qDebug() << "TwitterPlugin could not parse node out of the tweet"; - continue; - } - else - qDebug() << "TwitterPlugin parsed node " << node << " out of the tweet"; - - if ( status.user().screenName() == myScreenName && node == Database::instance()->dbid() ) - { - qDebug() << "My screen name and my dbid found; ignoring"; - continue; - } - - QHash< QString, QVariant > peerData; - if( m_cachedPeers.contains( status.user().screenName() ) ) - { - peerData = m_cachedPeers[status.user().screenName()].toHash(); - //force a re-send of info but no need to re-register - peerData["resend"] = QVariant::fromValue< bool >( true ); - } - peerData["node"] = QVariant::fromValue< QString >( node ); - QMetaObject::invokeMethod( this, "registerOffer", Q_ARG( QString, status.user().screenName() ), QGenericArgument( "QHash< QString, QVariant >", (const void*)&peerData ) ); - } + parseGotTomahawk( regex, status.user().screenName(), status.text() ); } TomahawkSettings::instance()->setTwitterCachedMentionsSinceId( m_cachedMentionsSinceId ); @@ -471,6 +475,9 @@ TwitterPlugin::directMessages( const QList< QTweetDMStatus > &messages ) { qDebug() << Q_FUNC_INFO; + QRegExp regex( QString( "^(@[a-zA-Z0-9]+ )?(Got Tomahawk\\?) (\\{[a-fA-F0-9\\-]+\\}) (.*)$" ), Qt::CaseSensitive, QRegExp::RegExp2 ); + QString myScreenName = TomahawkSettings::instance()->twitterScreenName(); + QHash< QString, QTweetDMStatus > latestHash; foreach ( QTweetDMStatus status, messages ) { @@ -488,50 +495,55 @@ TwitterPlugin::directMessages( const QList< QTweetDMStatus > &messages ) qDebug() << "TwitterPlugin checking direct message from " << status.senderScreenName() << " with content " << status.text(); if ( status.id() > m_cachedDirectMessagesSinceId ) m_cachedDirectMessagesSinceId = status.id(); - QStringList splitList = status.text().split(':'); - qDebug() << "TwitterPlugin found " << splitList.length() << " parts to the message; the parts are:"; - foreach( QString part, splitList ) - qDebug() << part; - if ( splitList.length() != 5 ) - continue; - if ( splitList[0] != "TOMAHAWKPEER" ) - continue; - if ( !splitList[1].startsWith( "Host=" ) || !splitList[2].startsWith( "Port=" ) || !splitList[3].startsWith( "Node=" ) || !splitList[4].startsWith( "PKey=" ) ) - continue; - int port = splitList[2].mid( 5 ).toInt(); - if ( port == 0 ) - continue; - QString host = splitList[1].mid( 5 ); - QString node = splitList[3].mid( 5 ); - QString pkey = splitList[4].mid( 5 ); - QStringList splitNode = node.split('*'); - if ( splitNode.length() != 2 ) + + if ( regex.exactMatch( status.text() ) ) + parseGotTomahawk( regex, status.sender().screenName(), status.text() ); + else { - qDebug() << "Old-style node info found, ignoring"; - continue; - } - qDebug() << "TwitterPlugin found a peerstart message from " << status.senderScreenName() << " with host " << host << " and port " << port << " and pkey " << pkey << " and node " << splitNode[0] << " destined for node " << splitNode[1]; + QStringList splitList = status.text().split(':'); + qDebug() << "TwitterPlugin found " << splitList.length() << " parts to the message; the parts are:"; + foreach( QString part, splitList ) + qDebug() << part; + if ( splitList.length() != 5 ) + continue; + if ( splitList[0] != "TOMAHAWKPEER" ) + continue; + if ( !splitList[1].startsWith( "Host=" ) || !splitList[2].startsWith( "Port=" ) || !splitList[3].startsWith( "Node=" ) || !splitList[4].startsWith( "PKey=" ) ) + continue; + int port = splitList[2].mid( 5 ).toInt(); + if ( port == 0 ) + continue; + QString host = splitList[1].mid( 5 ); + QString node = splitList[3].mid( 5 ); + QString pkey = splitList[4].mid( 5 ); + QStringList splitNode = node.split('*'); + if ( splitNode.length() != 2 ) + { + qDebug() << "Old-style node info found, ignoring"; + continue; + } + qDebug() << "TwitterPlugin found a peerstart message from " << status.senderScreenName() << " with host " << host << " and port " << port << " and pkey " << pkey << " and node " << splitNode[0] << " destined for node " << splitNode[1]; + - - QHash< QString, QVariant > peerData = ( m_cachedPeers.contains( status.senderScreenName() ) ) ? - m_cachedPeers[status.senderScreenName()].toHash() : - QHash< QString, QVariant >(); - - peerData["host"] = QVariant::fromValue< QString >( host ); - peerData["port"] = QVariant::fromValue< int >( port ); - peerData["pkey"] = QVariant::fromValue< QString >( pkey ); - peerData["node"] = QVariant::fromValue< QString >( splitNode[0] ); - peerData["dirty"] = QVariant::fromValue< bool >( true ); + QHash< QString, QVariant > peerData = ( m_cachedPeers.contains( status.senderScreenName() ) ) ? + m_cachedPeers[status.senderScreenName()].toHash() : + QHash< QString, QVariant >(); + + peerData["host"] = QVariant::fromValue< QString >( host ); + peerData["port"] = QVariant::fromValue< int >( port ); + peerData["pkey"] = QVariant::fromValue< QString >( pkey ); + peerData["node"] = QVariant::fromValue< QString >( splitNode[0] ); + peerData["dirty"] = QVariant::fromValue< bool >( true ); - QMetaObject::invokeMethod( this, "registerOffer", Q_ARG( QString, status.senderScreenName() ), QGenericArgument( "QHash< QString, QVariant >", (const void*)&peerData ) ); + QMetaObject::invokeMethod( this, "registerOffer", Q_ARG( QString, status.senderScreenName() ), QGenericArgument( "QHash< QString, QVariant >", (const void*)&peerData ) ); - if ( Database::instance()->dbid().startsWith( splitNode[1] ) ) - { - qDebug() << "TwitterPlugin found message destined for this node; destroying it"; - if ( !m_directMessageDestroy.isNull() ) - m_directMessageDestroy.data()->destroyMessage( status.id() ); + if ( Database::instance()->dbid().startsWith( splitNode[1] ) ) + { + qDebug() << "TwitterPlugin found message destined for this node; destroying it"; + if ( !m_directMessageDestroy.isNull() ) + m_directMessageDestroy.data()->destroyMessage( status.id() ); + } } - } TomahawkSettings::instance()->setTwitterCachedDirectMessagesSinceId( m_cachedDirectMessagesSinceId ); diff --git a/src/sip/twitter/twitter.h b/src/sip/twitter/twitter.h index e0c38b479..839ec88c3 100644 --- a/src/sip/twitter/twitter.h +++ b/src/sip/twitter/twitter.h @@ -92,6 +92,7 @@ private slots: private: bool refreshTwitterAuth(); + void parseGotTomahawk( const QRegExp ®ex, const QString &screenName, const QString &text ); QWeakPointer< TomahawkOAuthTwitter > m_twitterAuth; QWeakPointer< QTweetFriendsTimeline > m_friendsTimeline; diff --git a/src/sip/twitter/twitterconfigwidget.cpp b/src/sip/twitter/twitterconfigwidget.cpp index dbf484652..15e38c56d 100644 --- a/src/sip/twitter/twitterconfigwidget.cpp +++ b/src/sip/twitter/twitterconfigwidget.cpp @@ -26,6 +26,7 @@ #include "tomahawkoauthtwitter.h" #include #include +#include #include @@ -39,8 +40,11 @@ TwitterConfigWidget::TwitterConfigWidget(SipPlugin* plugin, QWidget *parent) : connect(ui->twitterAuthenticateButton, SIGNAL(pressed()), this, SLOT(authDeauthTwitter())); connect(ui->twitterTweetGotTomahawkButton, SIGNAL(pressed()), - this, SLOT(startPostGotTomahawkStatus())); - + this, SLOT(startPostGlobalGotTomahawkStatus())); + connect(ui->twitterUserTweetButton, SIGNAL(pressed()), + this, SLOT(startPostUserGotTomahawkStatus())); + connect(ui->twitterDirectTweetButton, SIGNAL(pressed()), + this, SLOT(startPostDirectGotTomahawkStatus())); TomahawkSettings* s = TomahawkSettings::instance(); if ( s->twitterOAuthToken().isEmpty() || s->twitterOAuthTokenSecret().isEmpty() || s->twitterScreenName().isEmpty() ) @@ -48,7 +52,14 @@ TwitterConfigWidget::TwitterConfigWidget(SipPlugin* plugin, QWidget *parent) : ui->twitterStatusLabel->setText("Status: No saved credentials"); ui->twitterAuthenticateButton->setText( "Authenticate" ); ui->twitterInstructionsInfoLabel->setVisible( false ); + ui->twitterGlobalTweetLabel->setVisible( false ); + ui->twitterUserTweetLabel->setVisible( false ); + ui->twitterDirectTweetLabel->setVisible( false ); ui->twitterTweetGotTomahawkButton->setVisible( false ); + ui->twitterUserTweetButton->setVisible( false ); + ui->twitterUserTweetLineEdit->setVisible( false ); + ui->twitterDirectTweetButton->setVisible( false ); + ui->twitterDirectTweetLineEdit->setVisible( false ); emit twitterAuthed( false ); } @@ -57,7 +68,14 @@ TwitterConfigWidget::TwitterConfigWidget(SipPlugin* plugin, QWidget *parent) : ui->twitterStatusLabel->setText("Status: Credentials saved"); ui->twitterAuthenticateButton->setText( "De-authenticate" ); ui->twitterInstructionsInfoLabel->setVisible( true ); + ui->twitterGlobalTweetLabel->setVisible( true ); + ui->twitterUserTweetLabel->setVisible( true ); + ui->twitterDirectTweetLabel->setVisible( true ); ui->twitterTweetGotTomahawkButton->setVisible( true ); + ui->twitterUserTweetButton->setVisible( true ); + ui->twitterUserTweetLineEdit->setVisible( true ); + ui->twitterDirectTweetButton->setVisible( true ); + ui->twitterDirectTweetLineEdit->setVisible( true ); emit twitterAuthed( true ); } @@ -115,7 +133,14 @@ TwitterConfigWidget::authenticateVerifyReply( const QTweetUser &user ) ui->twitterStatusLabel->setText("Status: Credentials saved"); ui->twitterAuthenticateButton->setText( "De-authenticate" ); ui->twitterInstructionsInfoLabel->setVisible( true ); + ui->twitterGlobalTweetLabel->setVisible( true ); + ui->twitterUserTweetLabel->setVisible( true ); + ui->twitterDirectTweetLabel->setVisible( true ); ui->twitterTweetGotTomahawkButton->setVisible( true ); + ui->twitterUserTweetButton->setVisible( true ); + ui->twitterUserTweetLineEdit->setVisible( true ); + ui->twitterDirectTweetButton->setVisible( true ); + ui->twitterDirectTweetLineEdit->setVisible( true ); m_plugin->connectPlugin( false ); @@ -132,7 +157,6 @@ TwitterConfigWidget::authenticateVerifyError( QTweetNetBase::ErrorCode code, con return; } - void TwitterConfigWidget::deauthenticateTwitter() { @@ -145,11 +169,49 @@ TwitterConfigWidget::deauthenticateTwitter() ui->twitterStatusLabel->setText("Status: No saved credentials"); ui->twitterAuthenticateButton->setText( "Authenticate" ); ui->twitterInstructionsInfoLabel->setVisible( false ); + ui->twitterGlobalTweetLabel->setVisible( false ); + ui->twitterUserTweetLabel->setVisible( false ); + ui->twitterDirectTweetLabel->setVisible( false ); ui->twitterTweetGotTomahawkButton->setVisible( false ); + ui->twitterUserTweetButton->setVisible( false ); + ui->twitterUserTweetLineEdit->setVisible( false ); + ui->twitterDirectTweetButton->setVisible( false ); + ui->twitterDirectTweetLineEdit->setVisible( false ); emit twitterAuthed( false ); } +void +TwitterConfigWidget::startPostGlobalGotTomahawkStatus() +{ + m_postGTtype = "global"; + startPostGotTomahawkStatus(); +} + +void +TwitterConfigWidget::startPostUserGotTomahawkStatus() +{ + if ( ui->twitterUserTweetLineEdit->text().isEmpty() || ui->twitterUserTweetLineEdit->text() == "@" ) + { + QMessageBox::critical( 0, QString("Tweetin' Error"), QString("You cannot leave the user name empty when sending a mention.") ); + return; + } + m_postGTtype = "user"; + startPostGotTomahawkStatus(); +} + +void +TwitterConfigWidget::startPostDirectGotTomahawkStatus() +{ + if ( ui->twitterDirectTweetLineEdit->text().isEmpty() || ui->twitterDirectTweetLineEdit->text() == "@" ) + { + QMessageBox::critical( 0, QString("Tweetin' Error"), QString("You cannot leave the user name empty when sending a direct message.") ); + return; + } + m_postGTtype = "direct"; + startPostGotTomahawkStatus(); +} + void TwitterConfigWidget::startPostGotTomahawkStatus() { @@ -170,7 +232,6 @@ TwitterConfigWidget::startPostGotTomahawkStatus() credVerifier->verify(); } - void TwitterConfigWidget::postGotTomahawkStatusAuthVerifyReply( const QTweetUser &user ) { @@ -186,14 +247,36 @@ TwitterConfigWidget::postGotTomahawkStatusAuthVerifyReply( const QTweetUser &use twitAuth->setNetworkAccessManager( TomahawkUtils::nam() ); twitAuth->setOAuthToken( s->twitterOAuthToken().toLatin1() ); twitAuth->setOAuthTokenSecret( s->twitterOAuthTokenSecret().toLatin1() ); - QTweetStatusUpdate *statUpdate = new QTweetStatusUpdate( twitAuth, this ); - connect( statUpdate, SIGNAL( postedStatus(const QTweetStatus &) ), SLOT( postGotTomahawkStatusUpdateReply(const QTweetStatus &) ) ); - connect( statUpdate, SIGNAL( error(QTweetNetBase::ErrorCode, const QString&) ), SLOT( postGotTomahawkStatusUpdateError(QTweetNetBase::ErrorCode, const QString &) ) ); - QString uuid = QUuid::createUuid(); - statUpdate->post( QString( "Got Tomahawk? {" ) + Database::instance()->dbid() + QString( "} (" ) + uuid.mid( 1, 8 ) + QString( ")" ) + QString( " http://gettomahawk.com" ) ); + if ( m_postGTtype != "direct" ) + { + QTweetStatusUpdate *statUpdate = new QTweetStatusUpdate( twitAuth, this ); + connect( statUpdate, SIGNAL( postedStatus(const QTweetStatus &) ), SLOT( postGotTomahawkStatusUpdateReply(const QTweetStatus &) ) ); + connect( statUpdate, SIGNAL( error(QTweetNetBase::ErrorCode, const QString&) ), SLOT( postGotTomahawkStatusUpdateError(QTweetNetBase::ErrorCode, const QString &) ) ); + QString uuid = QUuid::createUuid(); + QString message = QString( "Got Tomahawk? {" ) + Database::instance()->dbid() + QString( "} (" ) + uuid.mid( 1, 8 ) + QString( ")" ) + QString( " http://gettomahawk.com" ); + if ( m_postGTtype == "user" ) + { + QString user = ui->twitterUserTweetLineEdit->text(); + if ( user.startsWith( "@" ) ) + user.remove( 0, 1 ); + message = QString( "@" ) + user + QString( " " ) + message; + } + statUpdate->post( message ); + } + else + { + QTweetDirectMessageNew *statUpdate = new QTweetDirectMessageNew( twitAuth, this ); + connect( statUpdate, SIGNAL( parsedDirectMessage(const QTweetDMStatus &)), SLOT( postGotTomahawkDirectMessageReply(const QTweetDMStatus &) ) ); + connect( statUpdate, SIGNAL( error(QTweetNetBase::ErrorCode, const QString&) ), SLOT( postGotTomahawkStatusUpdateError(QTweetNetBase::ErrorCode, const QString &) ) ); + QString uuid = QUuid::createUuid(); + QString message = QString( "Got Tomahawk? {" ) + Database::instance()->dbid() + QString( "} (" ) + uuid.mid( 1, 8 ) + QString( ")" ) + QString( " http://gettomahawk.com" ); + QString user = ui->twitterDirectTweetLineEdit->text(); + if ( user.startsWith( "@" ) ) + user.remove( 0, 1 ); + statUpdate->post( user, message ); + } } - void TwitterConfigWidget::postGotTomahawkStatusUpdateReply( const QTweetStatus& status ) { @@ -203,6 +286,14 @@ TwitterConfigWidget::postGotTomahawkStatusUpdateReply( const QTweetStatus& statu QMessageBox::information( 0, QString("Tweeted!"), QString("Your tweet has been posted!") ); } +void +TwitterConfigWidget::postGotTomahawkDirectMessageReply( const QTweetDMStatus& status ) +{ + if ( status.id() == 0 ) + QMessageBox::critical( 0, QString("Tweetin' Error"), QString("There was an error posting your direct message -- sorry!") ); + else + QMessageBox::information( 0, QString("Tweeted!"), QString("Your message has been posted!") ); +} void TwitterConfigWidget::postGotTomahawkStatusUpdateError( QTweetNetBase::ErrorCode code, const QString& errorMsg ) diff --git a/src/sip/twitter/twitterconfigwidget.h b/src/sip/twitter/twitterconfigwidget.h index 5521c7f7b..3d18db321 100644 --- a/src/sip/twitter/twitterconfigwidget.h +++ b/src/sip/twitter/twitterconfigwidget.h @@ -22,6 +22,7 @@ #include "sip/SipPlugin.h" #include +#include #include #include @@ -45,19 +46,24 @@ signals: private slots: void authDeauthTwitter(); + void startPostGlobalGotTomahawkStatus(); + void startPostUserGotTomahawkStatus(); + void startPostDirectGotTomahawkStatus(); void startPostGotTomahawkStatus(); void authenticateVerifyReply( const QTweetUser &user ); void authenticateVerifyError( QTweetNetBase::ErrorCode code, const QString &errorMsg ); void postGotTomahawkStatusAuthVerifyReply( const QTweetUser &user ); void postGotTomahawkStatusUpdateReply( const QTweetStatus &status ); + void postGotTomahawkDirectMessageReply( const QTweetDMStatus &status ); void postGotTomahawkStatusUpdateError( QTweetNetBase::ErrorCode, const QString &errorMsg ); private: void authenticateTwitter(); void deauthenticateTwitter(); - + Ui::TwitterConfigWidget *ui; SipPlugin *m_plugin; + QString m_postGTtype; }; #endif // TWITTERCONFIGWIDGET_H diff --git a/src/sip/twitter/twitterconfigwidget.ui b/src/sip/twitter/twitterconfigwidget.ui index f7109915a..52c99105b 100644 --- a/src/sip/twitter/twitterconfigwidget.ui +++ b/src/sip/twitter/twitterconfigwidget.ui @@ -65,9 +65,9 @@ - Here's how it works: just press the button below to tweet "Got Tomahawk?" and some necessary information. Then be (very) patient. Twitter is an asynchronous protocol so it can take a bit! + Here's how it works: just press one of the buttons below to tweet "Got Tomahawk?" and some necessary information. Then be (very) patient. Twitter is an asynchronous protocol so it can take a bit! -If connections to peers seem to have been lost, just press the button again to re-post a tweet for resynchronization. +If connections to peers seem to have been lost, just press the appropriate button again to re-post a tweet for resynchronization. true @@ -75,11 +75,163 @@ If connections to peers seem to have been lost, just press the button again to r - - - Press here to have Tomahawk post a tweet + + + Qt::Vertical - + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + + + + 0 + 0 + + + + Use this button to send a normal, public tweet: + + + + + + + + + + + + + + + 0 + 0 + + + + Press here to post a public tweet + + + + + + + + + + + Use this button to send a public @mention to the user you enter: + + + + + + + + + + + + + + 0 + 0 + + + + + 250 + 0 + + + + e.g. @tomahawkplayer + + + + + + + + 0 + 0 + + + + Press here to post a public @mention + + + + + + + + + + + + + Use this button to send a private, direct message to the user you enter: + + + + + + + + + + + + + + 0 + 0 + + + + + 250 + 0 + + + + e.g. tomahawkplayer + + + + + + + + 0 + 0 + + + + Press here to post a private message + + + + + + + + + +