Skip to content

Commit

Permalink
Add feature to toot to Mastodon if tweeted on Twitter. fix #421 @8.0h
Browse files Browse the repository at this point in the history
  • Loading branch information
みぞ@CrazyBeatCoder committed Aug 19, 2018
1 parent 4efdf17 commit b9db55e
Show file tree
Hide file tree
Showing 8 changed files with 482 additions and 0 deletions.
19 changes: 19 additions & 0 deletions account-activity-api-beta-samples.iml
Expand Up @@ -10,6 +10,7 @@
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
Expand All @@ -27,6 +28,24 @@
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.8.9" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.8.0" level="project" />
<orderEntry type="library" name="Maven: org.twitter4j:twitter4j-core:4.0.7" level="project" />
<orderEntry type="library" name="Maven: com.github.sys1yagi.mastodon4j:mastodon4j:1.6.0" level="project" />
<orderEntry type="library" name="Maven: org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.0" level="project" />
<orderEntry type="library" name="Maven: org.jetbrains.kotlin:kotlin-stdlib:1.2.0" level="project" />
<orderEntry type="library" name="Maven: org.jetbrains:annotations:13.0" level="project" />
<orderEntry type="library" name="Maven: com.squareup.okhttp3:okhttp:3.6.0" level="project" />
<orderEntry type="library" name="Maven: com.squareup.okio:okio:1.11.0" level="project" />
<orderEntry type="library" name="Maven: com.google.code.gson:gson:2.8.0" level="project" />
<orderEntry type="library" name="Maven: com.github.sys1yagi.mastodon4j:mastodon4j-rx:1.6.0" level="project" />
<orderEntry type="library" name="Maven: io.reactivex.rxjava2:rxjava:2.0.8" level="project" />
<orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.0" level="project" />
<orderEntry type="library" name="Maven: commons-codec:commons-codec:1.11" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.12" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.mockito:mockito-all:1.10.19" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.google.appengine:appengine-testing:1.9.60" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.google.appengine:appengine-api-stubs:1.9.60" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.google.appengine:appengine-tools-sdk:1.9.60" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.google.truth:truth:0.39" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.google.errorprone:error_prone_annotations:2.1.3" level="project" />
</component>
</module>
60 changes: 60 additions & 0 deletions pom.xml
Expand Up @@ -21,6 +21,14 @@ Copyright 2015 Google Inc.
<version>2.0-SNAPSHOT</version>
<groupId>com.mizo0203.twitter.account.activity.api.beta.samples</groupId>
<artifactId>account-activity-api-beta-samples</artifactId>

<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>

<dependencies>
<dependency>
<groupId>com.google.appengine</groupId>
Expand Down Expand Up @@ -81,12 +89,64 @@ Copyright 2015 Google Inc.
</dependency>
<!-- [END Twitter4J] -->

<!-- [START mastodon4j] -->
<dependency>
<groupId>com.github.sys1yagi.mastodon4j</groupId>
<artifactId>mastodon4j</artifactId>
<version>[1.6,)</version>
</dependency>
<dependency>
<groupId>com.github.sys1yagi.mastodon4j</groupId>
<artifactId>mastodon4j-rx</artifactId>
<version>[1.6,)</version>
</dependency>
<!-- [END mastodon4j] -->

<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>

<!-- Test Dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-testing</artifactId>
<version>1.9.60</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-api-stubs</artifactId>
<version>1.9.60</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-tools-sdk</artifactId>
<version>1.9.60</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.truth</groupId>
<artifactId>truth</artifactId>
<version>0.39</version>
<scope>test</scope>
</dependency>

</dependencies>
<build>
<!-- for hot reload of the web application -->
Expand Down
@@ -1,6 +1,7 @@
package com.mizo0203.twitter.account.activity.api.beta.samples;

import com.mizo0203.twitter.account.activity.api.beta.samples.domain.difine.KeysAndAccessTokens;
import com.mizo0203.twitter.account.activity.api.beta.samples.repo.GoogleAppEngineMastodonClient;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import twitter4j.*;
Expand Down Expand Up @@ -81,5 +82,27 @@ private void parse(String source) {
*/
private void onTweetCreateEvent(long forUserId, Status status) {
LOG.log(Level.INFO, "onTweetCreateEvent forUserId: " + forUserId + " status: " + status);

// 自分のツイートで無ければ無視する (自分のリツイートも無視する)
if (status.getUser().getId() != forUserId || status.isRetweet()) {
return;
}

// 他の Twitter ユーザ宛のツイートであれば無視する
long inReplyToUserId = status.getInReplyToUserId();
if (inReplyToUserId != -1 && inReplyToUserId != forUserId) {
return;
}

com.sys1yagi.mastodon4j.api.entity.Status mastodonStatus =
new GoogleAppEngineMastodonClient()
.postStatus(
status.getText(),
null,
null,
null,
null,
com.sys1yagi.mastodon4j.api.entity.Status.Visibility.Public);
LOG.log(Level.INFO, "onTweetCreateEvent mastodonStatus: " + mastodonStatus);
}
}
Expand Up @@ -6,4 +6,7 @@ public class KeysAndAccessTokens {
public static final String CONSUMER_SECRET = "";
public static final String TOKEN = "";
public static final String TOKEN_SECRET = "";

public static final String MASTODON_INSTANCE_NAME = "";
public static final String MASTODON_ACCESS_TOKEN = "";
}
@@ -0,0 +1,110 @@
package com.mizo0203.twitter.account.activity.api.beta.samples.repo;

import com.google.gson.Gson;
import com.mizo0203.twitter.account.activity.api.beta.samples.domain.difine.KeysAndAccessTokens;
import com.sys1yagi.mastodon4j.MastodonClient;
import com.sys1yagi.mastodon4j.api.entity.Status;
import com.sys1yagi.mastodon4j.api.exception.Mastodon4jRequestException;
import com.sys1yagi.mastodon4j.api.method.Statuses;
import okhttp3.GoogleAppEngineOkHttpClient;
import okhttp3.Headers;
import okhttp3.OkHttpClient;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.reflect.Field;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

public class GoogleAppEngineMastodonClient {

private static final Logger LOG = Logger.getLogger(GoogleAppEngineMastodonClient.class.getName());
private final MastodonClient mMastodonClient;

public GoogleAppEngineMastodonClient() {

OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
mMastodonClient =
new MastodonClient.Builder(
KeysAndAccessTokens.MASTODON_INSTANCE_NAME, okHttpClientBuilder, new Gson())
.build();

Headers headers =
new Headers.Builder()
.set(
"Authorization",
String.format("Bearer %s", KeysAndAccessTokens.MASTODON_ACCESS_TOKEN))
.build();

try {
Field field = MastodonClient.class.getDeclaredField("client");
field.setAccessible(true);
field.set(
mMastodonClient,
new GoogleAppEngineOkHttpClient.Builder(okHttpClientBuilder).headers(headers).build());
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}

/**
* Posting a new status
*
* <p>POST /api/v1/statuses
*
* <p>注:重複ステータスを防止するため、このエンドポイントはIdempotency-Keyヘッダーを受け取ります。 これは新しいステータスごとに一意の文字列に設定する必要があります。
* ネットワークエラーが発生した場合、同じIdempotency-Keyで要求を再試行できます。
* 同じIdempotency-Keyを持つリクエストの数にかかわらず、1つのステータスだけが作成されます。
*
* <p>idempotencyとidempotencyの詳細については、https://stripe.com/blog/idempotencyを参照してください。
*
* <p>Note: In order to prevent duplicate statuses, this endpoint accepts an Idempotency-Key
* header, which should be set to a unique string for each new status. In the event of a network
* error, a request can be retried with the same Idempotency-Key. Only one status will be created
* regardless of how many requests with the same Idempotency-Key did go through.
*
* <p>See https://stripe.com/blog/idempotency for more on idempotency and idempotency keys.
*
* <p>https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#posting-a-new-status
*
* @param status: The text of the status
* @param inReplyToId (optional): local ID of the status you want to reply to
* @param mediaIds (optional): array of media IDs to attach to the status (maximum 4)
* @param sensitive (optional): set this to mark the media of the status as NSFW
* @param spoilerText (optional): text to be shown as a warning before the actual content
* @param visibility (optional): either "direct", "private", "unlisted" or "public"
* @return the new Status
*/
public Status postStatus(
@Nonnull String status,
@Nullable Long inReplyToId,
@Nullable List<Long> mediaIds,
@Nullable Boolean sensitive,
@Nullable String spoilerText,
@Nullable Status.Visibility visibility) {
try {
if (sensitive == null) {
sensitive = Boolean.FALSE;
}
Status ret;
if (visibility == null) {
ret =
new Statuses(mMastodonClient)
.postStatus(status, inReplyToId, mediaIds, sensitive, spoilerText)
.execute();
} else {
ret =
new Statuses(mMastodonClient)
.postStatus(status, inReplyToId, mediaIds, sensitive, spoilerText, visibility)
.execute();
}
LOG.log(Level.INFO, "postStatus ret: " + ret);
return ret;
} catch (Mastodon4jRequestException e) {
LOG.log(Level.SEVERE, "postStatus", e);
LOG.log(Level.SEVERE, "postStatus ErrorResponse: ", e.getResponse());
return null;
}
}
}

0 comments on commit b9db55e

Please sign in to comment.