Regularly post imaged tweets on Twitter with AWS Lambda + Java

Overview

--Write sample code to regularly post tweets with images on AWS Lambda + Java 8 --Draw the current date and time in Japanese on the prepared background image --Use the Twitter4J library for posting to Twitter --Use Amazon CloudWatch Events for regular execution

Use Google Noto Fonts to draw Japanese on images

--Japanese fonts are not available on AWS Lambda + Java --Reference: Check if there is a font that can use Japanese \ (Hiragana / Katakana / Kanji ) in AWS Lambda \ + Java -Qiita --Incorporate and use Noto Sans CJK JP Regular fonts from Google Noto Fonts --Font file NotoSansCJKjp-Regular.otf is about 16.4MB in size --Reference: Noto CJK – Google Noto Fonts

Source code

File list

--pom.xml: Build configuration file for Maven --ImageTweet.java: Java class that performs the main processing --NotoSansCJKjp-Regular.otf: Font file containing Japanese characters --fukidashi.png: Background image file of the image to be posted on 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>> {

  /**
   *This is the entry point when running as a standalone Java application.
   *
   * @param args First element[0]An array with a string to tweet to
   */
  public static void main(String[] args) {
    Map<String, Object> output = new ImageTweet().invoke(new HashMap<String, Object>());
    System.out.println(output);
  }

  /**
   *Entry point when executing AWS Lambda function(Handler method)is.
   *
   * @param input input data
   * @param context AWS Lambda Context object
   * @return output data
   */
  @Override
  public Map<String, Object> handleRequest(Map<String, Object> input, Context context) {
    return invoke(input);
  }

  /**
   *Tweet
   *
   * @param input Text to tweet
   */
  public Map<String, Object> invoke(Map<String, Object> input) {
    Map output = new HashMap<String, Object>();
    try {
      //Assemble the text to tweet
      String text = LocalDateTime.now(ZoneId.of("JST", ZoneId.SHORT_IDS))
        .format(DateTimeFormatter.ofPattern("Year M month d day H hour m minutes s seconds"));

      //Image file path
      String imagePath = "image/fukidashi.png ";
      //Image file name
      String imageFileName = new File(imagePath).getName();
      //Image format name
      String imageFormatName = "png";
      //Load image file
      BufferedImage image = getImageFromResource(imagePath);

      //Read font file
      String fontPath = "font/NotoSansCJKjp-Regular.otf";
      Font font = getFont(fontPath);

      //Process the image(Draw text)
      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();
      //Get image data
      InputStream imageBody = createInputStream(image, imageFormatName);

      //Get environment variables
      Map<String, String> env = System.getenv();

      //Tweet
      TwitterFactory tf = getTwitterFactory(env);
      Status status = tweet(tf, text, imageFileName, imageBody);

      //Build output data
      output.put("result", status);
    } catch (Exception e) {
      output.put("error", e);
    }

    return output;
  }

  /**
   *Load the font file.
   *
   * @param path font file path
   * @return font object
   * @throws FontFormatException When the required font data is not available
   * @throws IOException if any error occurs
   */
  private Font getFont(String path) throws FontFormatException, IOException {
    ClassLoader cl = getClass().getClassLoader();
    try (InputStream input = cl.getResourceAsStream(path)) {
      //The default size of Java Font objects is 12
      return Font.createFont(Font.TRUETYPE_FONT, input).deriveFont(12.0f);
    }
  }

  /**
   *Load the image file.
   *
   * @param path Image file path
   * @return image object
   * @throws IOException if any error occurs
   */
  private BufferedImage getImageFromResource(String path) throws IOException {
    ClassLoader cl = getClass().getClassLoader();
    URL url = cl.getResource(path);
    return ImageIO.read(url);
  }

  /**
   *Gets the byte string of image data.
   *
   * @param image image object
   * @param formatName Image format name
   * @return Image data byte sequence
   * @throws IOException if any error occurs
   */
  private InputStream createInputStream(BufferedImage image, String formatName) throws IOException {
    ByteArrayOutputStream output = new ByteArrayOutputStream();
    ImageIO.write(image, formatName, output);
    return new ByteArrayInputStream(output.toByteArray());
  }

  /**
   *Gets a TwitterFactory object.
   *
   * @param env environment variable
   * @return TwitterFactory object
   */
  private TwitterFactory getTwitterFactory(Map<String, String> env) {
    ConfigurationBuilder cb = new ConfigurationBuilder();
    //Get Twitter credentials from environment variables
    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());
  }

  /**
   *Post a tweet with an image.
   *
   * @param text The text to tweet
   * @param imageFileName File name of the image to be included in the tweet
   * @param imageBody Image data to include in tweets
   * @return Status object as a result of tweeting
   * @throws TwitterException If any error occurs
   */
  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

Configuration file for building with Maven.

--Match Java version 1.8 supported by AWS Lambda --Introduce the library aws-lambda-java-core for AWS Lambda --Introduce maven-shade-plugin to generate JAR file including library

<?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>

background image

In this program, a balloon image is prepared as the background of the image to be tweeted.

image-tweet-00.png

JAR file generation

Build with Maven and generate a JAR file.

$ mvn package

A JAR file called image-tweet-1.0.jar is generated in the target directory.

Creating an AWS Lambda function

Create a Lambda function from the Lambda Management Console (https://console.aws.amazon.com/lambda/).

Enter the "basic information". Function name: any name Runtime: Java 8 Run Role: Create a new role with basic Lambda permissions

image-tweet-03.jpg

Enter the "function code". Code entry type: Upload .zip or jar file Runtime: Java 8 Handler: com.example.ImageTweet :: handleRequest

Click "Upload" to upload the generated JAR file image-tweet-1.0.jar.

image-tweet-05.jpg

Set environment variables. In this program, the credential information for calling the Twitter API is obtained from environment variables.

image-tweet-06.jpg

Click Test at the top right of the page to create a new test event. Event template: Hello World Event name: any name Change the JSON that is the input data to any one (or leave the default).

image-tweet-08.jpg

You can run the test by clicking "Test" at the top right of the page. The result is displayed.

image-tweet-09.jpg

This completes the function for posting tweets with images on Twitter.

Use Amazon CloudWatch Events

Click Add Trigger on the left side of the Lambda function Designer.

image-tweet-07.jpg

Select CloudWatch Events.

image-tweet-04.jpg

Set the content of the trigger to execute every minute. Rule: Create a new rule Rule name: any name Rule description: Arbitrary description Rule type: Scheduled expression Scheduled expression: cron (* * * *? *)

image-tweet-02.jpg

This completes the settings that are executed periodically.

Execution result

You will be able to post tweets with images to Twitter on a regular basis.

image-tweet-01.jpg

Reference material

-Building Lambda functions with Java -AWS Lambda -What is Amazon CloudWatch Events -Amazon CloudWatch Events -Schedule expression using Rate or Cron -AWS Lambda -Rule Schedule Expression -Amazon CloudWatch Events -Google Developers Japan: Japanese, Chinese and Korean have been added to the beautiful open source Noto font family.

Recommended Posts

Regularly post imaged tweets on Twitter with AWS Lambda + Java
Is Java on AWS Lambda slow?
Hello World on AWS Lambda + Java
AWS Lambda with Java starting now Part 1
How to use Java framework with AWS Lambda! ??
Regularly notify Slack of articles stocked on Qiita with AWS Lambda + DynamoDB + CloudWatch
How to deploy Java to AWS Lambda with Serverless Framework
Create a SlackBot with AWS lambda & API Gateway in Java
Build AWS Lambda with Quarkus
With [AWS] CodeStar, you can build a Spring (Java) project running on Lambda in just 3 minutes! !!
Try running SlackBot made with Ruby x Sinatra on AWS Lambda
Until INSERT S3 object into EC2 DB with Lambda @ java: AWS
Install Java with zip on Windows
Using Java with AWS Lambda-Eclipse Preparation
Getting started with Java lambda expressions
Run C binaries on AWS Lambda
Using Java with AWS Lambda-Implementation-Check CloudWatch Arguments
Using Java with AWS Lambda-Implementation-Stop / Launch EC2
Try running Word2vec model on AWS Lambda
Using JupyterLab + Java with WSL on Windows 10
Java version control with jenv on OSX
Build Java 8 development environment on AWS Cloud9
Install Java8 with Yum on Amazon Linux
Try managing Java libraries with AWS CodeArtifact
Build OpenCV with Java Wrapper on Ubuntu 18.04
Working with huge JSON in Java Lambda
Build an environment with Docker on AWS
You can do it right away with Serverless Framework Serverless on AWS (API + Lambda [java] is easy to set up)