使用Perl构建Twitter机器人

继上周关于使用Perl构建Reddit机器人的文章之后,让我们看看如何使用Perl构建一个Twitter机器人。正如你所预期的,Perl使这变得容易,但在我们进入代码之前,让我们谈谈优点。

将推文自动化添加到现有应用程序中可以带来许多好处。首先,它可以节省时间,让你专注于其他更有价值的活动。自动化可以保护你免受手动转录错误(如拼写错误和损坏的URL)的影响。自动化还意味着增加推文数量更便宜,在其他条件相同的情况下,更高的推文数量将导致更多的Twitter粉丝。听起来不错?太好了,那么让我们来看看代码吧!

编写推文

编写推文的核心代码非常简单。我正在使用Net::Twitter::Lite发行版,它支持Twitter的最新版本API

use strict;
use warnings;
use Net::Twitter::Lite::WithAPIv1_1;

sub tweet
{
  my ($text) = @_;

  my $twitter = Net::Twitter::Lite::WithAPIv1_1->new(
    access_token_secret => $ENV{TWITTER_ACCESS_SECRET},
    consumer_secret     => $ENV{TWITTER_CONSUMER_SECRET},
    access_token        => $ENV{TWITTER_ACCESS_TOKEN},
    consumer_key        => $ENV{TWITTER_CONSUMER_KEY},
    user_agent          => 'TwitterBotExample',
    ssl => 1,
  );
  $twitter->update($text);
}

代码导入Net::Twitter::Lite::WithAPIv1_1以使用新的Twitter API。子程序tweet接受一些文本作为参数。然后它创建一个新的Net::Twitter::Lite::WithAPIv1_1对象,使用环境变量作为凭证。如果你还没有这些凭证,你可以免费注册你自己的Twitter账户并生成令牌。最后,子程序调用update方法来推文。

现在我可以通过在代码中添加这一行来发送一条推文

tweet("This is a computer speaking!");

安全第一

到目前为止一切顺利,但是这段代码并不安全。如果未提供$text作为参数,或者我们的环境变量未声明,或者调用Twitter失败,会发生什么?我将添加一些检查来处理这些场景。

use strict;
use warnings;
use Net::Twitter::Lite::WithAPIv1_1;
use Try::Tiny;

sub tweet
{
  my ($text) = @_;

  die 'tweet requires text as an argument' unless $text;

  unless ($ENV{TWITTER_CONSUMER_KEY}
          && $ENV{TWITTER_CONSUMER_SECRET}
          && $ENV{TWITTER_ACCESS_TOKEN}
          && $ENV{TWITTER_ACCESS_SECRET})
  {
    die 'Required Twitter Env vars are not all defined';
  }

  try
  {
    my $twitter = Net::Twitter::Lite::WithAPIv1_1->new(
      access_token_secret => $ENV{TWITTER_ACCESS_SECRET},
      consumer_secret     => $ENV{TWITTER_CONSUMER_SECRET},
      access_token        => $ENV{TWITTER_ACCESS_TOKEN},
      consumer_key        => $ENV{TWITTER_CONSUMER_KEY},
      user_agent          => 'TwitterBotExample',
      ssl => 1,
    );
    $twitter->update($text);
  }
  catch
  {
    die join(' ', "Error tweeting $text",
                   $_->code, $_->message, $_->error);
  };
}

这段代码与之前大致相同,但现在它会在处理之前检查所需变量。代码还导入了Try::Tiny,因为我已经在Twitter代码周围添加了try/catch块。如果在Twitter交互中抛出异常,则激活catch块。由于Net::Twitter::Lite抛出结构化异常,catch块通过从结构化异常中提取信息来构建异常字符串,然后自己调用die

你可能想知道是否真的需要调用die。我们能否只是返回undef而不是退出程序?调用die的优点是,tweet子程序的调用者可以更好地决定如何处理该问题,因此我们将该决定推迟给他们。如果调用代码没有正确处理die,我们知道程序将退出。但如果我们返回undef,我们就不会有这样的保证。但这并不意味着代码必须退出。让我们假设我有一百多条推文要发送,也许我只是想将错误记录在某个地方并继续进行。

foreach my $text (@tweet_texts)
{
  try
  {
    tweet($text);
  }
  catch
  {
    log_error($_);
  };
}

如果我正在打印一系列推文,其中顺序很重要,我仍然可以记录错误,但然后调用die来退出程序。

foreach my $text (@sequence_of_texts)
{
  try
  {
    tweet($text);
  }
  catch
  {
    log_error($_);
    die $_; # exit the program
  };
}

更好的文本处理

现在代码更安全了,我们还能如何改进它?一个著名的限制是推文长度不能超过140个字符。现在,如果tweet()子程序收到了一个超过140个字符的文本字符串,Twitter API将拒绝它,引发异常,然后代码将退出。我认为我们可以做得更好。

当我思考我发送的推文内容时,我通常会在推文中分享关于Perl的文章链接。这些链接通常包括一些文本、一个URL和一个话题标签。将它们分别传递给tweet()函数是很有用的,因为为了使所有内容都适应,你可能需要截断文本,但你不想截断URL或话题标签,因为这样可能会改变其意义,或者破坏URL。

use strict;
use warnings;
use Net::Twitter::Lite::WithAPIv1_1;
use Try::Tiny;

sub tweet
{
  my ($text, $url, $hashtag) = @_;

  unless ($text && $url && $hashtag)
  {
    die 'tweet requires text, url and hashtag arguments';
  }

  unless ($ENV{TWITTER_CONSUMER_KEY}
          && $ENV{TWITTER_CONSUMER_SECRET}
          && $ENV{TWITTER_ACCESS_TOKEN}
          && $ENV{TWITTER_ACCESS_SECRET})
  {
    die 'Required Twitter Env vars are not all defined';
  }

  # build tweet, max 140 chars
  my $tweet;
  
  if (length("$text $hashtag") < 118)
  {
    $tweet = "$text $url $hashtag";
  }
  elsif (length($text) < 118)
  {
    $tweet = "$text $url";
  }
  else # shorten text, drop the hashtag
  {
    $tweet = substr($text, 0, 113) . "... " . $url;
  }

  try
  {
    my $twitter = Net::Twitter::Lite::WithAPIv1_1->new(
      access_token_secret => $ENV{TWITTER_ACCESS_SECRET},
      consumer_secret     => $ENV{TWITTER_CONSUMER_SECRET},
      access_token        => $ENV{TWITTER_ACCESS_TOKEN},
      consumer_key        => $ENV{TWITTER_CONSUMER_KEY},
      user_agent          => 'TwitterBotExample',
      ssl => 1,
    );
    $twitter->update($tweet);
  }
  catch
  {
    die join(' ', "Error tweeting $text $url $hashtag",
                   $_->code, $_->message, $_->error);
  };
}

Twitter将URL视为长度为12个字符。现在代码会检查我们参数的长度,如果需要,会截断$text。只有当有足够的空间时,话题标签才会被包含。

这段代码对我来说是有效的,但你可能想要做一些不同的处理。Twitter凭据可以存储在一个配置文件中,而不是环境变量中。$hashtag参数可以是一个包含话题标签的数组引用,这些标签可以逐步添加到推文文本中,而不是一个单一的文本字符串,这样可以避免全部或无的取舍。


这篇文章最初发布在PerlTricks.com上。

标签

David Farrell

David是一位专业程序员,他经常推文并在博客上关于代码和编程艺术发表文章。

浏览他们的文章

反馈

这篇文章有什么问题吗?请帮助我们通过在GitHub上打开一个问题或拉取请求来解决。