概要
- AWS Lambda + Java 8 で画像付きツイートを定期的に投稿するサンプルコードを書く
- 用意した背景画像に現在日時を日本語で描画する
- Twitter への投稿は Twitter4J ライブラリを使用する
- 定期的な実行は Amazon CloudWatch Events を使用する
画像への日本語描画には Google Noto Fonts を使用
- AWS Lambda + Java 上では日本語が使えるフォントが用意されていない
- Google Noto Fonts の Noto Sans CJK JP Regular フォントを組み込んで使用する
- フォントファイル NotoSansCJKjp-Regular.otf のサイズは約16.4MB
- 参考: Noto CJK – 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>
背景画像
今回のプログラムでは、ツイートする画像の背景としてフキダシ画像を用意している。
JAR ファイルの生成
Maven でビルドして JAR ファイルを生成する。
$ mvn package
target ディレクトリに image-tweet-1.0.jar という JAR ファイルが生成される。
AWS Lambda 関数の作成
Lambda Management Console から Lambda 関数を作成する。
「基本的な情報」を入力する。
関数名: 任意の名前
ランタイム: Java 8
実行ロール: 基本的な Lambda アクセス権限で新しいロールを作成
「関数コード」を入力する。
コード エントリ タイプ: .zip または jar ファイルをアップロード
ランタイム: Java 8
ハンドラ: com.example.ImageTweet::handleRequest
「アップロード」をクリックして、生成しておいた JAR ファイル image-tweet-1.0.jar をアップロードする。

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

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

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

ここまでで Twitter に画像付きツイートを投稿する機能が完成。
Amazon CloudWatch Events を使用する
Lambda 関数の Designer 左側にある 「トリガーを追加」 をクリックする。

CloudWatch Events を選択する。

毎分実行するようにトリガーの内容を設定する。
ルール: 新規ルールの作成
ルール名: 任意の名前
ルールの説明: 任意の説明文
ルールタイプ: スケジュール式
スケジュール式: cron(* * * * ? *)
これで定期実行される設定が完了する。
実行結果
Twitter に画像付きツイートを定期的に投稿できるようになる。