LoginSignup
4
4

More than 3 years have passed since last update.

AWS Lambda + Java で Twitter に画像付きツイートを定期的に投稿する

Last updated at Posted at 2019-09-01

概要

  • AWS Lambda + Java 8 で画像付きツイートを定期的に投稿するサンプルコードを書く
  • 用意した背景画像に現在日時を日本語で描画する
  • Twitter への投稿は Twitter4J ライブラリを使用する
  • 定期的な実行は Amazon CloudWatch Events を使用する

画像への日本語描画には Google Noto Fonts を使用

ソースコード

ファイル一覧

  • pom.xml : Maven 用ビルド設定ファイル
  • ImageTweet.java : メインの処理を実行する Java クラス
  • NotoSansCJKjp-Regular.otf : 日本語の文字を含むフォントファイル
  • fukidashi.png : Twitter に投稿する画像の背景画像ファイル
├── pom.xml
└── src
    └── main
        ├── java
        │   └── com
        │       └── example
        │           └── ImageTweet.java
        └── resources
            ├── font
            │   └── NotoSansCJKjp-Regular.otf
            └── image
                └── fukidashi.png

ImageTweet.java

package com.example;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import twitter4j.Status;
import twitter4j.StatusUpdate;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.conf.ConfigurationBuilder;

import javax.imageio.ImageIO;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

public class ImageTweet implements RequestHandler<Map<String, Object>, Map<String, Object>> {

  /**
   * スタンドアローンの Java アプリケーションとして実行した場合のエントリポイントです。
   *
   * @param args 先頭の要素[0]にツイートする文字列を持つ配列
   */
  public static void main(String[] args) {
    Map<String, Object> output = new ImageTweet().invoke(new HashMap<String, Object>());
    System.out.println(output);
  }

  /**
   * AWS Labmda 関数を実行した場合のエントリポイント (ハンドラーメソッド) です。
   *
   * @param input   入力データ
   * @param context AWS Lambda Context オブジェクト
   * @return 出力データ
   */
  @Override
  public Map<String, Object> handleRequest(Map<String, Object> input, Context context) {
    return invoke(input);
  }

  /**
   * ツイートします。
   *
   * @param input ツイートするテキスト
   */
  public Map<String, Object> invoke(Map<String, Object> input) {
    Map output = new HashMap<String, Object>();
    try {
      // ツイートするテキストを組み立てる
      String text = LocalDateTime.now(ZoneId.of("JST", ZoneId.SHORT_IDS))
        .format(DateTimeFormatter.ofPattern("y年M月d日 H時m分s秒"));

      // 画像ファイルのパス
      String imagePath = "image/fukidashi.png";
      // 画像ファイル名
      String imageFileName = new File(imagePath).getName();
      // 画像フォーマット名
      String imageFormatName = "png";
      // 画像ファイルを読み込む
      BufferedImage image = getImageFromResource(imagePath);

      // フォントファイルを読み込む
      String fontPath = "font/NotoSansCJKjp-Regular.otf";
      Font font = getFont(fontPath);

      // 画像を加工する (テキストを描画)
      Graphics2D g = image.createGraphics();
      g.setColor(Color.BLUE);
      g.setFont(font.deriveFont(30.0f));
      g.drawString(text, image.getWidth() / 4, image.getHeight() / 2);
      g.dispose();
      // 画像のデータを取得
      InputStream imageBody = createInputStream(image, imageFormatName);

      // 環境変数を取得する
      Map<String, String> env = System.getenv();

      // ツイートする
      TwitterFactory tf = getTwitterFactory(env);
      Status status = tweet(tf, text, imageFileName, imageBody);

      // 出力データを構築
      output.put("result", status);
    } catch (Exception e) {
      output.put("error", e);
    }

    return output;
  }

  /**
   * フォントファイルを読み込みます。
   *
   * @param path フォントファイルのパス
   * @return フォントオブジェクト
   * @throws FontFormatException 必要なフォントデータが無い場合
   * @throws IOException         何らかのエラーが発生した場合
   */
  private Font getFont(String path) throws FontFormatException, IOException {
    ClassLoader cl = getClass().getClassLoader();
    try (InputStream input = cl.getResourceAsStream(path)) {
      // Java の Font オブジェクトのデフォルトサイズは12
      return Font.createFont(Font.TRUETYPE_FONT, input).deriveFont(12.0f);
    }
  }

  /**
   * 画像ファイルを読み込みます。
   *
   * @param path 画像ファイルのパス
   * @return 画像オブジェクト
   * @throws IOException 何らかのエラーが発生した場合
   */
  private BufferedImage getImageFromResource(String path) throws IOException {
    ClassLoader cl = getClass().getClassLoader();
    URL url = cl.getResource(path);
    return ImageIO.read(url);
  }

  /**
   * 画像データのバイト列を取得します。
   *
   * @param image      画像オブジェクト
   * @param formatName 画像フォーマット名
   * @return 画像データのバイト列
   * @throws IOException 何らかのエラーが発生した場合
   */
  private InputStream createInputStream(BufferedImage image, String formatName) throws IOException {
    ByteArrayOutputStream output = new ByteArrayOutputStream();
    ImageIO.write(image, formatName, output);
    return new ByteArrayInputStream(output.toByteArray());
  }

  /**
   * TwitterFactory オブジェクトを取得します。
   *
   * @param env 環境変数
   * @return TwitterFactory オブジェクト
   */
  private TwitterFactory getTwitterFactory(Map<String, String> env) {
    ConfigurationBuilder cb = new ConfigurationBuilder();
    // Twitter 認証情報を環境変数から取得する
    cb.setDebugEnabled(true)
      .setOAuthConsumerKey(env.get("TWITTER_CONSUMER_KEY"))
      .setOAuthConsumerSecret(env.get("TWITTER_CONSUMER_SECRET"))
      .setOAuthAccessToken(env.get("TWITTER_ACCESS_TOKEN"))
      .setOAuthAccessTokenSecret(env.get("TWITTER_ACCESS_TOKEN_SECRET"));
    return new TwitterFactory(cb.build());
  }

  /**
   * 画像付きツイートを投稿します。
   *
   * @param text          ツイートするテキスト
   * @param imageFileName ツイートに含める画像のファイル名
   * @param imageBody     ツイートに含める画像のデータ
   * @return ツイートした結果の Status オブジェクト
   * @throws TwitterException 何らかのエラーが発生した場合
   */
  private Status tweet(TwitterFactory tf, String text, String imageFileName, InputStream imageBody) throws TwitterException {
    Twitter twitter = tf.getInstance();
    StatusUpdate status = new StatusUpdate(text);
    status.setMedia(imageFileName, imageBody);
    return twitter.updateStatus(status);
  }
}

pom.xml

Maven でビルドするための設定ファイル。

  • AWS Lambda でサポートされている Java のバージョン 1.8 に合わせる
  • AWS Lambda 用ライブラリ aws-lambda-java-core を導入する
  • ライブラリも含めた JAR ファイルを生成するため maven-shade-plugin を導入する
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>image-tweet</artifactId>
  <packaging>jar</packaging>
  <version>1.0</version>
  <name>image-tweet</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>
    <!-- https://mvnrepository.com/artifact/com.amazonaws/aws-lambda-java-core -->
    <dependency>
      <groupId>com.amazonaws</groupId>
      <artifactId>aws-lambda-java-core</artifactId>
      <version>1.2.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.twitter4j/twitter4j-core -->
    <dependency>
      <groupId>org.twitter4j</groupId>
      <artifactId>twitter4j-core</artifactId>
      <version>4.0.7</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <!-- https://maven.apache.org/plugins/maven-shade-plugin/ -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.1</version>
        <configuration>
          <createDependencyReducedPom>false</createDependencyReducedPom>
        </configuration>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

</project>

背景画像

今回のプログラムでは、ツイートする画像の背景としてフキダシ画像を用意している。

image-tweet-00.png

JAR ファイルの生成

Maven でビルドして JAR ファイルを生成する。

$ mvn package

target ディレクトリに image-tweet-1.0.jar という JAR ファイルが生成される。

AWS Lambda 関数の作成

Lambda Management Console から Lambda 関数を作成する。

「基本的な情報」を入力する。
関数名: 任意の名前
ランタイム: Java 8
実行ロール: 基本的な Lambda アクセス権限で新しいロールを作成

image-tweet-03.jpg

「関数コード」を入力する。
コード エントリ タイプ: .zip または jar ファイルをアップロード
ランタイム: Java 8
ハンドラ: com.example.ImageTweet::handleRequest

「アップロード」をクリックして、生成しておいた JAR ファイル image-tweet-1.0.jar をアップロードする。

image-tweet-05.jpg

環境変数を設定する。
今回のプログラムでは Twitter API をコールするためのクレデンシャル情報を環境変数から取得している。

image-tweet-06.jpg

ページ右上の「テスト」をクリックして、新しいテストイベントを作成する。
イベントテンプレート: Hello World
イベント名: 任意の名前
入力データとなる JSON は任意のものに変更する (あるいはデフォルトのまま)。

image-tweet-08.jpg

ページ右上の「テスト」をクリックすると、テストを実行できる。結果が表示される。

image-tweet-09.jpg

ここまでで Twitter に画像付きツイートを投稿する機能が完成。

Amazon CloudWatch Events を使用する

Lambda 関数の Designer 左側にある 「トリガーを追加」 をクリックする。

image-tweet-07.jpg

CloudWatch Events を選択する。

image-tweet-04.jpg

毎分実行するようにトリガーの内容を設定する。
ルール: 新規ルールの作成
ルール名: 任意の名前
ルールの説明: 任意の説明文
ルールタイプ: スケジュール式
スケジュール式: cron(* * * * ? *)

image-tweet-02.jpg

これで定期実行される設定が完了する。

実行結果

Twitter に画像付きツイートを定期的に投稿できるようになる。

image-tweet-01.jpg

参考資料

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4