リビジョン 6a3fcb6d
みぞ @mizo0203 さんが6年以上前に追加
.idea/artifacts/TimelineTalker_jar.xml | ||
---|---|---|
2 | 2 |
<artifact type="jar" name="TimelineTalker:jar"> |
3 | 3 |
<output-path>$PROJECT_DIR$/out/artifacts/TimelineTalker_jar</output-path> |
4 | 4 |
<root id="archive" name="TimelineTalker.jar"> |
5 |
<element id="module-output" name="TimelineTalker"/> |
|
6 |
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/twitter4j/twitter4j-core/4.0.6/twitter4j-core-4.0.6.jar" path-in-jar="/" /> |
|
7 |
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/twitter4j/twitter4j-stream/4.0.6/twitter4j-stream-4.0.6.jar" path-in-jar="/" /> |
|
5 |
<element id="module-output" name="TimelineTalker" /> |
|
6 |
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.8.0/gson-2.8.0.jar" path-in-jar="/" /> |
|
7 |
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/io/reactivex/rxjava2/rxjava/2.0.8/rxjava-2.0.8.jar" path-in-jar="/" /> |
|
8 |
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.2.0/kotlin-stdlib-1.2.0.jar" path-in-jar="/" /> |
|
9 |
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/com/github/sys1yagi/mastodon4j/mastodon4j/1.6.0/mastodon4j-1.6.0.jar" path-in-jar="/" /> |
|
10 |
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jre7/1.2.0/kotlin-stdlib-jre7-1.2.0.jar" path-in-jar="/" /> |
|
11 |
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" path-in-jar="/" /> |
|
12 |
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/com/github/sys1yagi/mastodon4j/mastodon4j-rx/1.6.0/mastodon4j-rx-1.6.0.jar" path-in-jar="/" /> |
|
13 |
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/com/squareup/okio/okio/1.11.0/okio-1.11.0.jar" path-in-jar="/" /> |
|
14 |
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/reactivestreams/reactive-streams/1.0.0/reactive-streams-1.0.0.jar" path-in-jar="/" /> |
|
15 |
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/twitter4j/twitter4j-core/4.0.7/twitter4j-core-4.0.7.jar" path-in-jar="/" /> |
|
16 |
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/commons-io/commons-io/2.6/commons-io-2.6.jar" path-in-jar="/" /> |
|
17 |
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/com/squareup/okhttp3/okhttp/3.6.0/okhttp-3.6.0.jar" path-in-jar="/" /> |
|
8 | 18 |
</root> |
9 | 19 |
</artifact> |
10 | 20 |
</component> |
TimelineTalker.iml | ||
---|---|---|
9 | 9 |
</content> |
10 | 10 |
<orderEntry type="inheritedJdk" /> |
11 | 11 |
<orderEntry type="sourceFolder" forTests="false" /> |
12 |
<orderEntry type="library" name="Maven: org.twitter4j:twitter4j-core:4.0.6" level="project" /> |
|
13 | 12 |
<orderEntry type="library" name="Maven: org.twitter4j:twitter4j-core:4.0.7" level="project" /> |
13 |
<orderEntry type="library" name="Maven: com.github.sys1yagi.mastodon4j:mastodon4j:1.6.0" level="project" /> |
|
14 |
<orderEntry type="library" name="Maven: org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.0" level="project" /> |
|
15 |
<orderEntry type="library" name="Maven: org.jetbrains.kotlin:kotlin-stdlib:1.2.0" level="project" /> |
|
16 |
<orderEntry type="library" name="Maven: org.jetbrains:annotations:13.0" level="project" /> |
|
17 |
<orderEntry type="library" name="Maven: com.squareup.okhttp3:okhttp:3.6.0" level="project" /> |
|
18 |
<orderEntry type="library" name="Maven: com.squareup.okio:okio:1.11.0" level="project" /> |
|
19 |
<orderEntry type="library" name="Maven: com.google.code.gson:gson:2.8.0" level="project" /> |
|
20 |
<orderEntry type="library" name="Maven: com.github.sys1yagi.mastodon4j:mastodon4j-rx:1.6.0" level="project" /> |
|
21 |
<orderEntry type="library" name="Maven: io.reactivex.rxjava2:rxjava:2.0.8" level="project" /> |
|
22 |
<orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.0" level="project" /> |
|
23 |
<orderEntry type="library" name="Maven: commons-io:commons-io:2.6" level="project" /> |
|
14 | 24 |
</component> |
15 | 25 |
</module> |
pom.xml | ||
---|---|---|
1 |
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
2 |
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
3 |
<modelVersion>4.0.0</modelVersion>
|
|
1 |
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
|
2 |
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
3 |
<modelVersion>4.0.0</modelVersion>
|
|
4 | 4 |
|
5 |
<groupId>com.mizo0203</groupId>
|
|
6 |
<artifactId>TimelineTalker</artifactId>
|
|
7 |
<version>0.0.1-SNAPSHOT</version>
|
|
8 |
<packaging>jar</packaging>
|
|
5 |
<groupId>com.mizo0203</groupId>
|
|
6 |
<artifactId>TimelineTalker</artifactId>
|
|
7 |
<version>0.0.1-SNAPSHOT</version>
|
|
8 |
<packaging>jar</packaging>
|
|
9 | 9 |
|
10 |
<properties>
|
|
11 |
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
12 |
</properties>
|
|
10 |
<properties>
|
|
11 |
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
12 |
</properties>
|
|
13 | 13 |
|
14 |
<build> |
|
15 |
<sourceDirectory>src</sourceDirectory> |
|
16 |
<outputDirectory>target/classes</outputDirectory> |
|
17 |
<resources> |
|
18 |
<resource> |
|
19 |
<directory>src</directory> |
|
20 |
<excludes> |
|
21 |
<exclude>**/*.java</exclude> |
|
22 |
</excludes> |
|
23 |
</resource> |
|
24 |
</resources> |
|
25 |
<plugins> |
|
26 |
<plugin> |
|
27 |
<artifactId>maven-compiler-plugin</artifactId> |
|
28 |
<version>3.1</version> |
|
29 |
<configuration> |
|
30 |
<source>1.7</source> |
|
31 |
<target>1.7</target> |
|
32 |
</configuration> |
|
33 |
</plugin> |
|
34 |
</plugins> |
|
35 |
</build> |
|
14 |
<repositories> |
|
15 |
<repository> |
|
16 |
<id>jitpack.io</id> |
|
17 |
<url>https://jitpack.io</url> |
|
18 |
</repository> |
|
19 |
</repositories> |
|
36 | 20 |
|
37 |
<dependencies> |
|
38 |
<dependency> |
|
39 |
<groupId>org.twitter4j</groupId> |
|
40 |
<artifactId>twitter4j-core</artifactId> |
|
41 |
<version>[4.0,)</version> |
|
42 |
</dependency> |
|
43 |
</dependencies> |
|
21 |
<build> |
|
22 |
<sourceDirectory>src</sourceDirectory> |
|
23 |
<outputDirectory>target/classes</outputDirectory> |
|
24 |
<resources> |
|
25 |
<resource> |
|
26 |
<directory>src</directory> |
|
27 |
<excludes> |
|
28 |
<exclude>**/*.java</exclude> |
|
29 |
</excludes> |
|
30 |
</resource> |
|
31 |
</resources> |
|
32 |
<plugins> |
|
33 |
<plugin> |
|
34 |
<artifactId>maven-compiler-plugin</artifactId> |
|
35 |
<version>3.1</version> |
|
36 |
<configuration> |
|
37 |
<source>1.7</source> |
|
38 |
<target>1.7</target> |
|
39 |
</configuration> |
|
40 |
</plugin> |
|
41 |
</plugins> |
|
42 |
</build> |
|
43 |
|
|
44 |
<dependencies> |
|
45 |
<dependency> |
|
46 |
<groupId>org.twitter4j</groupId> |
|
47 |
<artifactId>twitter4j-core</artifactId> |
|
48 |
<version>[4.0,)</version> |
|
49 |
</dependency> |
|
50 |
<dependency> |
|
51 |
<groupId>com.github.sys1yagi.mastodon4j</groupId> |
|
52 |
<artifactId>mastodon4j</artifactId> |
|
53 |
<version>[1.6,)</version> |
|
54 |
</dependency> |
|
55 |
<dependency> |
|
56 |
<groupId>com.github.sys1yagi.mastodon4j</groupId> |
|
57 |
<artifactId>mastodon4j-rx</artifactId> |
|
58 |
<version>[1.6,)</version> |
|
59 |
</dependency> |
|
60 |
|
|
61 |
<!-- https://mvnrepository.com/artifact/commons-io/commons-io --> |
|
62 |
<dependency> |
|
63 |
<groupId>commons-io</groupId> |
|
64 |
<artifactId>commons-io</artifactId> |
|
65 |
<version>2.6</version> |
|
66 |
</dependency> |
|
67 |
|
|
68 |
</dependencies> |
|
44 | 69 |
</project> |
src/META-INF/MANIFEST.MF | ||
---|---|---|
1 | 1 |
Manifest-Version: 1.0 |
2 | 2 |
Implementation-Title: TimelineTalker |
3 | 3 |
Implementation-Version: 1.1 |
4 |
Main-Class: com.mizo0203.timeline.talker.Application
|
|
4 |
Main-Class: com.mizo0203.timeline.talker.Main
|
|
5 | 5 |
|
src/com/mizo0203/timeline/talker/Application.java | ||
---|---|---|
1 |
package com.mizo0203.timeline.talker; |
|
2 |
|
|
3 |
import twitter4j.conf.Configuration; |
|
4 |
import twitter4j.conf.ConfigurationBuilder; |
|
5 |
|
|
6 |
/** |
|
7 |
* Java アプリケーション起動時に実行されるクラス |
|
8 |
* |
|
9 |
* @author みぞ@CrazyBeatCoder |
|
10 |
*/ |
|
11 |
public class Application { |
|
12 |
|
|
13 |
public static void main(String[] args) { |
|
14 |
TimelineTalker timelineTalker; |
|
15 |
Talker talker; |
|
16 |
|
|
17 |
try { |
|
18 |
talker = new Talker(); |
|
19 |
timelineTalker = |
|
20 |
new TimelineTalker(new Arguments(args).twitterConfiguration, talker); |
|
21 |
} catch (IllegalArgumentException | IllegalStateException e) { |
|
22 |
System.err.println(e.getMessage()); |
|
23 |
return; |
|
24 |
} |
|
25 |
|
|
26 |
timelineTalker.start(); |
|
27 |
talker.talkAsync("アプリケーションを起動しました", Talker.YukkuriVoice.REIMU); |
|
28 |
} |
|
29 |
|
|
30 |
/** |
|
31 |
* Java アプリケーション起動時に指定する引数のデータクラス |
|
32 |
* |
|
33 |
* @author みぞ@CrazyBeatCoder |
|
34 |
*/ |
|
35 |
private static class Arguments { |
|
36 |
|
|
37 |
private final Configuration twitterConfiguration; |
|
38 |
|
|
39 |
private Arguments(String[] args) throws IllegalArgumentException { |
|
40 |
if (args.length < Argument.values().length) { |
|
41 |
StringBuilder exceptionMessage = new StringBuilder(); |
|
42 |
exceptionMessage.append(Argument.values().length + " つの引数を指定してください。\n"); |
|
43 |
for (Argument arg : Argument.values()) { |
|
44 |
exceptionMessage.append((arg.ordinal() + 1) + " つ目: " + arg.detail + "\n"); |
|
45 |
} |
|
46 |
throw new IllegalArgumentException(exceptionMessage.toString()); |
|
47 |
} |
|
48 |
|
|
49 |
String consumer_key = args[Argument.CONSUMER_KEY.ordinal()]; |
|
50 |
String consumer_secret = args[Argument.CONSUMER_SECRET.ordinal()]; |
|
51 |
String access_token = args[Argument.ACCESS_TOKEN.ordinal()]; |
|
52 |
String access_token_secret = args[Argument.ACCESS_TOKEN_SECRET.ordinal()]; |
|
53 |
|
|
54 |
twitterConfiguration = new ConfigurationBuilder().setOAuthConsumerKey(consumer_key) |
|
55 |
.setOAuthConsumerSecret(consumer_secret).setOAuthAccessToken(access_token) |
|
56 |
.setOAuthAccessTokenSecret(access_token_secret).build(); |
|
57 |
} |
|
58 |
|
|
59 |
} |
|
60 |
|
|
61 |
/** |
|
62 |
* Java アプリケーション起動時に指定する引数の定義 |
|
63 |
* |
|
64 |
* @author みぞ@CrazyBeatCoder |
|
65 |
*/ |
|
66 |
private enum Argument { |
|
67 |
CONSUMER_KEY("Twitter Application's Consumer Key (API Key)"), // |
|
68 |
CONSUMER_SECRET("Twitter Application's Consumer Secret (API Secret)"), // |
|
69 |
ACCESS_TOKEN("Twitter Account's Access Token"), // |
|
70 |
ACCESS_TOKEN_SECRET("Twitter Account's Access Token Secret"), // |
|
71 |
; |
|
72 |
|
|
73 |
private final String detail; |
|
74 |
|
|
75 |
private Argument(String detail) { |
|
76 |
this.detail = detail; |
|
77 |
} |
|
78 |
} |
|
79 |
} |
src/com/mizo0203/timeline/talker/Arguments.java | ||
---|---|---|
1 |
package com.mizo0203.timeline.talker; |
|
2 |
|
|
3 |
import com.google.gson.Gson; |
|
4 |
import com.sys1yagi.mastodon4j.MastodonClient; |
|
5 |
import okhttp3.OkHttpClient; |
|
6 |
import org.jetbrains.annotations.NotNull; |
|
7 |
import org.jetbrains.annotations.Nullable; |
|
8 |
import twitter4j.conf.Configuration; |
|
9 |
import twitter4j.conf.ConfigurationBuilder; |
|
10 |
|
|
11 |
/** |
|
12 |
* Java アプリケーション起動時に指定する引数のデータクラス |
|
13 |
* |
|
14 |
* @author みぞ@CrazyBeatCoder |
|
15 |
*/ |
|
16 |
/* package */ class Arguments { |
|
17 |
|
|
18 |
@Nullable /* package */ final Configuration twitterConfiguration; |
|
19 |
@Nullable /* package */ final MastodonClient mastodonClient; |
|
20 |
|
|
21 |
/* package */ Arguments(String[] args) throws IllegalArgumentException { |
|
22 |
if (args.length == Argument.values().length) { |
|
23 |
twitterConfiguration = |
|
24 |
createTwitterConfiguration( |
|
25 |
args[Argument.TWITTER_CONSUMER_KEY.ordinal()], |
|
26 |
args[Argument.TWITTER_CONSUMER_SECRET.ordinal()], |
|
27 |
args[Argument.TWITTER_ACCESS_TOKEN.ordinal()], |
|
28 |
args[Argument.TWITTER_ACCESS_TOKEN_SECRET.ordinal()]); |
|
29 |
mastodonClient = |
|
30 |
createMastodonClient( |
|
31 |
args[Argument.MASTODON_INSTANCE_NAME.ordinal()], |
|
32 |
args[Argument.MASTODON_ACCESS_TOKEN.ordinal()]); |
|
33 |
} else if (args.length == Argument.Twitter.values().length) { |
|
34 |
twitterConfiguration = |
|
35 |
createTwitterConfiguration( |
|
36 |
args[Argument.Twitter.CONSUMER_KEY.ordinal()], |
|
37 |
args[Argument.Twitter.CONSUMER_SECRET.ordinal()], |
|
38 |
args[Argument.Twitter.ACCESS_TOKEN.ordinal()], |
|
39 |
args[Argument.Twitter.ACCESS_TOKEN_SECRET.ordinal()]); |
|
40 |
mastodonClient = null; |
|
41 |
} else if (args.length == Argument.Mastodon.values().length) { |
|
42 |
twitterConfiguration = null; |
|
43 |
mastodonClient = |
|
44 |
createMastodonClient( |
|
45 |
args[Argument.Mastodon.INSTANCE_NAME.ordinal()], |
|
46 |
args[Argument.Mastodon.ACCESS_TOKEN.ordinal()]); |
|
47 |
} else { |
|
48 |
throw createIllegalArgumentException(); |
|
49 |
} |
|
50 |
} |
|
51 |
|
|
52 |
private static Configuration createTwitterConfiguration( |
|
53 |
String twitterConsumerKey, |
|
54 |
String twitterConsumerSecret, |
|
55 |
String twitterAccessToken, |
|
56 |
String twitterAccessTokenSecret) { |
|
57 |
return new ConfigurationBuilder() |
|
58 |
.setOAuthConsumerKey(twitterConsumerKey) |
|
59 |
.setOAuthConsumerSecret(twitterConsumerSecret) |
|
60 |
.setOAuthAccessToken(twitterAccessToken) |
|
61 |
.setOAuthAccessTokenSecret(twitterAccessTokenSecret) |
|
62 |
.build(); |
|
63 |
} |
|
64 |
|
|
65 |
private static MastodonClient createMastodonClient( |
|
66 |
String mastodonInstanceName, String mastodonAccessToken) { |
|
67 |
return new MastodonClient.Builder(mastodonInstanceName, new OkHttpClient.Builder(), new Gson()) |
|
68 |
.accessToken(mastodonAccessToken) |
|
69 |
.useStreamingApi() |
|
70 |
.build(); |
|
71 |
} |
|
72 |
|
|
73 |
private static IllegalArgumentException createIllegalArgumentException() { |
|
74 |
return new IllegalArgumentException( |
|
75 |
Argument.createExceptionMessage() |
|
76 |
+ "\n" |
|
77 |
+ Argument.Twitter.createExceptionMessage() |
|
78 |
+ "\n" |
|
79 |
+ Argument.Mastodon.createExceptionMessage()); |
|
80 |
} |
|
81 |
|
|
82 |
/** |
|
83 |
* Java アプリケーション起動時に指定する引数の定義 |
|
84 |
* |
|
85 |
* @author みぞ@CrazyBeatCoder |
|
86 |
*/ |
|
87 |
private enum Argument { |
|
88 |
TWITTER_CONSUMER_KEY, // |
|
89 |
TWITTER_CONSUMER_SECRET, // |
|
90 |
TWITTER_ACCESS_TOKEN, // |
|
91 |
TWITTER_ACCESS_TOKEN_SECRET, // |
|
92 |
MASTODON_INSTANCE_NAME, // |
|
93 |
MASTODON_ACCESS_TOKEN, // |
|
94 |
; |
|
95 |
|
|
96 |
@NotNull |
|
97 |
private static String createExceptionMessage() { |
|
98 |
StringBuilder exceptionMessage = |
|
99 |
new StringBuilder("Twitter と Mastodon の両方を読み上げる場合、 ") |
|
100 |
.append(values().length) |
|
101 |
.append(" つの引数を指定してください。\n"); |
|
102 |
for (Argument arg : values()) { |
|
103 |
exceptionMessage |
|
104 |
.append(arg.ordinal() + 1) |
|
105 |
.append(" つ目: ") |
|
106 |
.append(arg.getDetail()) |
|
107 |
.append("\n"); |
|
108 |
} |
|
109 |
|
|
110 |
return exceptionMessage.toString(); |
|
111 |
} |
|
112 |
|
|
113 |
@NotNull |
|
114 |
private String getDetail() throws IllegalStateException { |
|
115 |
switch (this) { |
|
116 |
case TWITTER_CONSUMER_KEY: |
|
117 |
return "Twitter Application's Consumer Key (API Key)"; |
|
118 |
case TWITTER_CONSUMER_SECRET: |
|
119 |
return "Twitter Application's Consumer Secret (API Secret)"; |
|
120 |
case TWITTER_ACCESS_TOKEN: |
|
121 |
return "Twitter Account's Access Token"; |
|
122 |
case TWITTER_ACCESS_TOKEN_SECRET: |
|
123 |
return "Twitter Account's Access Token Secret"; |
|
124 |
case MASTODON_INSTANCE_NAME: |
|
125 |
return "Mastodon Instance Name"; |
|
126 |
case MASTODON_ACCESS_TOKEN: |
|
127 |
return "Mastodon Account's Access Token"; |
|
128 |
default: |
|
129 |
throw new IllegalStateException("getDetail this: " + this); |
|
130 |
} |
|
131 |
} |
|
132 |
|
|
133 |
private enum Twitter { |
|
134 |
CONSUMER_KEY, // |
|
135 |
CONSUMER_SECRET, // |
|
136 |
ACCESS_TOKEN, // |
|
137 |
ACCESS_TOKEN_SECRET, // |
|
138 |
; |
|
139 |
|
|
140 |
@NotNull |
|
141 |
private static String createExceptionMessage() { |
|
142 |
StringBuilder exceptionMessage = |
|
143 |
new StringBuilder("Twitter のみを読み上げる場合、 ") |
|
144 |
.append(values().length) |
|
145 |
.append(" つの引数を指定してください。\n"); |
|
146 |
for (Argument.Twitter arg : values()) { |
|
147 |
exceptionMessage |
|
148 |
.append(arg.ordinal() + 1) |
|
149 |
.append(" つ目: ") |
|
150 |
.append(arg.getDetail()) |
|
151 |
.append("\n"); |
|
152 |
} |
|
153 |
return exceptionMessage.toString(); |
|
154 |
} |
|
155 |
|
|
156 |
@NotNull |
|
157 |
private String getDetail() throws IllegalStateException { |
|
158 |
switch (this) { |
|
159 |
case CONSUMER_KEY: |
|
160 |
return TWITTER_CONSUMER_KEY.getDetail(); |
|
161 |
case CONSUMER_SECRET: |
|
162 |
return TWITTER_CONSUMER_SECRET.getDetail(); |
|
163 |
case ACCESS_TOKEN: |
|
164 |
return TWITTER_ACCESS_TOKEN.getDetail(); |
|
165 |
case ACCESS_TOKEN_SECRET: |
|
166 |
return TWITTER_ACCESS_TOKEN_SECRET.getDetail(); |
|
167 |
default: |
|
168 |
throw new IllegalStateException("getDetail this: " + this); |
|
169 |
} |
|
170 |
} |
|
171 |
} |
|
172 |
|
|
173 |
private enum Mastodon { |
|
174 |
INSTANCE_NAME, // |
|
175 |
ACCESS_TOKEN, // |
|
176 |
; |
|
177 |
|
|
178 |
@NotNull |
|
179 |
private static String createExceptionMessage() { |
|
180 |
StringBuilder exceptionMessage = |
|
181 |
new StringBuilder("Mastodon のみを読み上げる場合、 ") |
|
182 |
.append(values().length) |
|
183 |
.append(" つの引数を指定してください。\n"); |
|
184 |
for (Argument.Mastodon arg : values()) { |
|
185 |
exceptionMessage |
|
186 |
.append(arg.ordinal() + 1) |
|
187 |
.append(" つ目: ") |
|
188 |
.append(arg.getDetail()) |
|
189 |
.append("\n"); |
|
190 |
} |
|
191 |
return exceptionMessage.toString(); |
|
192 |
} |
|
193 |
|
|
194 |
@NotNull |
|
195 |
private String getDetail() throws IllegalStateException { |
|
196 |
switch (this) { |
|
197 |
case INSTANCE_NAME: |
|
198 |
return MASTODON_INSTANCE_NAME.getDetail(); |
|
199 |
case ACCESS_TOKEN: |
|
200 |
return MASTODON_ACCESS_TOKEN.getDetail(); |
|
201 |
default: |
|
202 |
throw new IllegalStateException("getDetail this: " + this); |
|
203 |
} |
|
204 |
} |
|
205 |
} |
|
206 |
} |
|
207 |
} |
src/com/mizo0203/timeline/talker/Main.java | ||
---|---|---|
1 |
package com.mizo0203.timeline.talker; |
|
2 |
|
|
3 |
import org.jetbrains.annotations.NotNull; |
|
4 |
|
|
5 |
/** |
|
6 |
* Java アプリケーション起動時に実行されるクラス |
|
7 |
* |
|
8 |
* @author みぞ@CrazyBeatCoder |
|
9 |
*/ |
|
10 |
public class Main { |
|
11 |
|
|
12 |
public static void main(@NotNull String[] args) { |
|
13 |
TimelineTalker twitterTimelineTalker; |
|
14 |
TimelineTalker mastodonTimelineTalker; |
|
15 |
Talker talker; |
|
16 |
|
|
17 |
try { |
|
18 |
Arguments arguments = new Arguments(args); |
|
19 |
talker = new Talker(); |
|
20 |
|
|
21 |
if (arguments.twitterConfiguration != null) { |
|
22 |
twitterTimelineTalker = new TwitterTimelineTalker(arguments.twitterConfiguration, talker); |
|
23 |
twitterTimelineTalker.start(); |
|
24 |
} |
|
25 |
|
|
26 |
if (arguments.mastodonClient != null) { |
|
27 |
mastodonTimelineTalker = new MastodonTimelineTalker(arguments.mastodonClient, talker); |
|
28 |
mastodonTimelineTalker.start(); |
|
29 |
} |
|
30 |
|
|
31 |
} catch (@NotNull IllegalArgumentException | IllegalStateException e) { |
|
32 |
System.err.println(e.getMessage()); |
|
33 |
return; |
|
34 |
} |
|
35 |
|
|
36 |
talker.talkAlternatelyAsync("アプリケーションを起動しました"); |
|
37 |
} |
|
38 |
} |
src/com/mizo0203/timeline/talker/MastodonTimelineTalker.java | ||
---|---|---|
1 |
package com.mizo0203.timeline.talker; |
|
2 |
|
|
3 |
import com.mizo0203.timeline.talker.util.DisplayNameUtil; |
|
4 |
import com.mizo0203.timeline.talker.util.HTMLParser; |
|
5 |
import com.mizo0203.timeline.talker.util.UrlUtil; |
|
6 |
import com.sys1yagi.mastodon4j.MastodonClient; |
|
7 |
import com.sys1yagi.mastodon4j.api.Handler; |
|
8 |
import com.sys1yagi.mastodon4j.api.entity.Account; |
|
9 |
import com.sys1yagi.mastodon4j.api.entity.Notification; |
|
10 |
import com.sys1yagi.mastodon4j.api.entity.Status; |
|
11 |
import com.sys1yagi.mastodon4j.api.method.Streaming; |
|
12 |
import org.jetbrains.annotations.NotNull; |
|
13 |
|
|
14 |
import java.io.IOException; |
|
15 |
import java.nio.charset.StandardCharsets; |
|
16 |
|
|
17 |
public class MastodonTimelineTalker implements TimelineTalker { |
|
18 |
|
|
19 |
@NotNull private final Streaming mStreaming; |
|
20 |
@NotNull private final OnStatusEvent mOnStatusEvent; |
|
21 |
|
|
22 |
/* package */ MastodonTimelineTalker(MastodonClient client, Talker talker) { |
|
23 |
mStreaming = new Streaming(client); |
|
24 |
mOnStatusEvent = new OnStatusEvent(talker); |
|
25 |
} |
|
26 |
|
|
27 |
@Override |
|
28 |
public void start() { |
|
29 |
|
|
30 |
try { |
|
31 |
mStreaming.user(mOnStatusEvent); |
|
32 |
} catch (Exception e) { |
|
33 |
e.printStackTrace(); |
|
34 |
} |
|
35 |
} |
|
36 |
|
|
37 |
private static class OnStatusEvent implements Handler { |
|
38 |
|
|
39 |
private final Talker mTalker; |
|
40 |
|
|
41 |
private OnStatusEvent(Talker talker) { |
|
42 |
mTalker = talker; |
|
43 |
} |
|
44 |
|
|
45 |
@Override |
|
46 |
public void onStatus(@NotNull Status status) { |
|
47 |
|
|
48 |
try { |
|
49 |
final StringBuffer buffer = new StringBuffer(); |
|
50 |
final Status reblogStatus = status.getReblog(); |
|
51 |
|
|
52 |
String displayName = "誰か"; |
|
53 |
if (status.getAccount() != null) { |
|
54 |
displayName = status.getAccount().getDisplayName(); |
|
55 |
} |
|
56 |
if (reblogStatus != null) { |
|
57 |
String reblogDisplayName = "誰か"; |
|
58 |
if (status.getAccount() != null) { |
|
59 |
displayName = status.getAccount().getDisplayName(); |
|
60 |
} |
|
61 |
buffer.append(DisplayNameUtil.removeContext(displayName)).append("さんがブースト。"); |
|
62 |
buffer.append(DisplayNameUtil.removeContext(reblogDisplayName)).append("さんから、"); |
|
63 |
buffer.append( |
|
64 |
new HTMLParser().parse(reblogStatus.getContent(), StandardCharsets.UTF_8, true)); |
|
65 |
} else { |
|
66 |
buffer.append(DisplayNameUtil.removeContext(displayName)).append("さんから、"); |
|
67 |
buffer.append(new HTMLParser().parse(status.getContent(), StandardCharsets.UTF_8, true)); |
|
68 |
} |
|
69 |
|
|
70 |
final String talkText = UrlUtil.convURLEmpty(buffer).replaceAll("\n", "。"); |
|
71 |
mTalker.talkAlternatelyAsync(talkText); |
|
72 |
} catch (IOException e) { |
|
73 |
e.printStackTrace(); |
|
74 |
} |
|
75 |
} |
|
76 |
|
|
77 |
@Override |
|
78 |
public void onNotification(@NotNull Notification notification) { |
|
79 |
final StringBuilder buffer = new StringBuilder(); |
|
80 |
final Account account = notification.getAccount(); |
|
81 |
|
|
82 |
if (account == null) { |
|
83 |
return; |
|
84 |
} |
|
85 |
|
|
86 |
switch (notification.getType()) { |
|
87 |
case "mention": |
|
88 |
buffer |
|
89 |
.append(DisplayNameUtil.removeContext(account.getDisplayName())) |
|
90 |
.append("さんがあなたをメンションしました。"); |
|
91 |
break; |
|
92 |
case "reblog": |
|
93 |
buffer |
|
94 |
.append(DisplayNameUtil.removeContext(account.getDisplayName())) |
|
95 |
.append("さんがあなたのトゥートをブーストしました。"); |
|
96 |
break; |
|
97 |
case "favourite": |
|
98 |
buffer |
|
99 |
.append(DisplayNameUtil.removeContext(account.getDisplayName())) |
|
100 |
.append("さんがあなたのトゥートをお気に入りに登録しました。"); |
|
101 |
break; |
|
102 |
case "follow": |
|
103 |
buffer |
|
104 |
.append(DisplayNameUtil.removeContext(account.getDisplayName())) |
|
105 |
.append("さんにフォローされました。"); |
|
106 |
break; |
|
107 |
default: |
|
108 |
return; |
|
109 |
} |
|
110 |
|
|
111 |
final String talkText = buffer.toString(); |
|
112 |
mTalker.talkAlternatelyAsync(talkText); |
|
113 |
} |
|
114 |
|
|
115 |
@Override |
|
116 |
public void onDelete(long id) { |
|
117 |
/* no op */ |
|
118 |
} |
|
119 |
} |
|
120 |
} |
src/com/mizo0203/timeline/talker/RuntimeUtil.java | ||
---|---|---|
1 |
package com.mizo0203.timeline.talker; |
|
2 |
|
|
3 |
import java.io.IOException; |
|
4 |
|
|
5 |
public class RuntimeUtil { |
|
6 |
|
|
7 |
public static void execute(String[] cmdarray) { |
|
8 |
try { |
|
9 |
Process process = Runtime.getRuntime().exec(cmdarray); |
|
10 |
process.waitFor(); |
|
11 |
process.destroy(); |
|
12 |
} catch (IOException | InterruptedException e) { |
|
13 |
e.printStackTrace(); |
|
14 |
} |
|
15 |
} |
|
16 |
|
|
17 |
} |
src/com/mizo0203/timeline/talker/Talker.java | ||
---|---|---|
1 | 1 |
package com.mizo0203.timeline.talker; |
2 | 2 |
|
3 |
import com.mizo0203.timeline.talker.util.RuntimeUtil; |
|
4 |
import org.jetbrains.annotations.NotNull; |
|
5 |
|
|
3 | 6 |
import java.io.*; |
4 | 7 |
import java.util.concurrent.ExecutorService; |
5 | 8 |
import java.util.concurrent.Executors; |
... | ... | |
10 | 13 |
|
11 | 14 |
private final ExecutorService mSingleThreadExecutor = Executors.newSingleThreadExecutor(); |
12 | 15 |
|
16 |
@NotNull private YukkuriVoice mNextVoice = Talker.YukkuriVoice.REIMU; |
|
17 |
|
|
13 | 18 |
public Talker() throws IllegalStateException, SecurityException { |
14 | 19 |
File file = new File(AQUESTALK_PI_PATH); |
15 | 20 |
if (!file.isFile()) { |
16 |
throw new IllegalStateException(file.getPath() + " に AquesTalk Pi がありません。\n" |
|
17 |
+ "https://www.a-quest.com/products/aquestalkpi.html\n" + "からダウンロードしてください。"); |
|
21 |
throw new IllegalStateException( |
|
22 |
file.getPath() |
|
23 |
+ " に AquesTalk Pi がありません。\n" |
|
24 |
+ "https://www.a-quest.com/products/aquestalkpi.html\n" |
|
25 |
+ "からダウンロードしてください。"); |
|
18 | 26 |
} |
19 | 27 |
if (!file.canExecute()) { |
20 | 28 |
throw new IllegalStateException(file.getPath() + " に実行権限がありません。"); |
21 | 29 |
} |
22 | 30 |
} |
23 | 31 |
|
24 |
public void talkAsync(final String text, final YukkuriVoice voice) { |
|
25 |
mSingleThreadExecutor.submit(new Runnable() { |
|
26 |
|
|
27 |
@Override |
|
28 |
public void run() { |
|
29 |
try { |
|
30 |
File file = new File("text.txt"); |
|
31 |
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file))); |
|
32 |
pw.println(text); |
|
33 |
pw.flush(); |
|
34 |
pw.close(); |
|
35 |
RuntimeUtil.execute(new String[] {AQUESTALK_PI_PATH, "-v", voice.value, "-f", "text.txt", |
|
36 |
"-o", "out.wav"}); |
|
37 |
RuntimeUtil.execute(new String[] {"sh", "-c", "aplay < out.wav"}); // 起動コマンドを指定する |
|
38 |
Thread.sleep(2000); |
|
39 |
} catch (IOException | InterruptedException e) { |
|
40 |
e.printStackTrace(); |
|
41 |
} |
|
42 |
} |
|
43 |
|
|
44 |
}); |
|
32 |
public void talkAlternatelyAsync(final String text) { |
|
33 |
mSingleThreadExecutor.submit( |
|
34 |
new Runnable() { |
|
35 |
|
|
36 |
@Override |
|
37 |
public void run() { |
|
38 |
try { |
|
39 |
File file = new File("text.txt"); |
|
40 |
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file))); |
|
41 |
pw.println(text); |
|
42 |
pw.flush(); |
|
43 |
pw.close(); |
|
44 |
RuntimeUtil.execute( |
|
45 |
new String[] { |
|
46 |
AQUESTALK_PI_PATH, "-v", mNextVoice.value, "-f", "text.txt", "-o", "out.wav" |
|
47 |
}); |
|
48 |
RuntimeUtil.execute(new String[] {"sh", "-c", "aplay < out.wav"}); // 起動コマンドを指定する |
|
49 |
|
|
50 |
// 読み上げは、霊夢と魔理沙が交互に行なう |
|
51 |
if (mNextVoice == Talker.YukkuriVoice.REIMU) { |
|
52 |
mNextVoice = Talker.YukkuriVoice.MARISA; |
|
53 |
} else { |
|
54 |
mNextVoice = Talker.YukkuriVoice.REIMU; |
|
55 |
} |
|
56 |
|
|
57 |
Thread.sleep(2000); |
|
58 |
} catch (@NotNull IOException | InterruptedException e) { |
|
59 |
e.printStackTrace(); |
|
60 |
} |
|
61 |
} |
|
62 |
}); |
|
45 | 63 |
} |
46 | 64 |
|
47 |
public static enum YukkuriVoice {
|
|
65 |
public enum YukkuriVoice { |
|
48 | 66 |
|
49 |
/** |
|
50 |
* ゆっくりボイス - 霊夢 |
|
51 |
*/ |
|
67 |
/** ゆっくりボイス - 霊夢 */ |
|
52 | 68 |
REIMU("f1"), // |
53 | 69 |
|
54 |
/** |
|
55 |
* ゆっくりボイス - 魔理沙 |
|
56 |
*/ |
|
70 |
/** ゆっくりボイス - 魔理沙 */ |
|
57 | 71 |
MARISA("f2"), // |
58 | 72 |
; |
59 | 73 |
|
60 | 74 |
private final String value; |
61 | 75 |
|
62 |
private YukkuriVoice(String value) {
|
|
76 |
YukkuriVoice(String value) { |
|
63 | 77 |
this.value = value; |
64 | 78 |
} |
65 | 79 |
} |
66 |
|
|
67 | 80 |
} |
src/com/mizo0203/timeline/talker/TimelineTalker.java | ||
---|---|---|
1 | 1 |
package com.mizo0203.timeline.talker; |
2 | 2 |
|
3 |
import twitter4j.*; |
|
4 |
import twitter4j.conf.Configuration; |
|
3 |
/* package */ interface TimelineTalker { |
|
5 | 4 |
|
6 |
import java.util.Collections; |
|
7 |
import java.util.Locale; |
|
8 |
import java.util.Timer; |
|
9 |
import java.util.TimerTask; |
|
10 |
import java.util.concurrent.TimeUnit; |
|
11 |
import java.util.regex.Matcher; |
|
12 |
import java.util.regex.Pattern; |
|
13 |
|
|
14 |
public class TimelineTalker { |
|
15 |
|
|
16 |
/** |
|
17 |
* ISO 639 言語コード - 日本語 (ja) |
|
18 |
*/ |
|
19 |
public static final String LANG_JA = Locale.JAPAN.getLanguage(); |
|
20 |
|
|
21 |
private final RequestHomeTimelineTimerTask mRequestHomeTimelineTimerTask; |
|
22 |
|
|
23 |
public TimelineTalker(Configuration configuration, Talker talker) { |
|
24 |
Twitter twitter = new TwitterFactory(configuration).getInstance(); |
|
25 |
mRequestHomeTimelineTimerTask = new RequestHomeTimelineTimerTask(twitter, talker); |
|
26 |
} |
|
27 |
|
|
28 |
private static String getUserNameWithoutContext(String name) { |
|
29 |
Pattern p = Pattern.compile("([^@@]+).+"); |
|
30 |
Matcher m = p.matcher(name); |
|
31 |
return m.replaceFirst("$1"); |
|
32 |
} |
|
33 |
|
|
34 |
public void start() { |
|
35 |
new Timer().schedule(mRequestHomeTimelineTimerTask, 0L, TimeUnit.MINUTES.toMillis(1)); |
|
36 |
} |
|
37 |
|
|
38 |
private static class RequestHomeTimelineTimerTask extends TimerTask { |
|
39 |
|
|
40 |
private static final int HOME_TIMELINE_COUNT_MAX = 200; |
|
41 |
private static final int HOME_TIMELINE_COUNT_MIN = 1; |
|
42 |
|
|
43 |
private final Twitter mTwitter; |
|
44 |
private final Talker mTalker; |
|
45 |
|
|
46 |
private Talker.YukkuriVoice mYukkuriVoice = Talker.YukkuriVoice.REIMU; |
|
47 |
|
|
48 |
/** |
|
49 |
* mStatusSinceId より大きい(つまり、より新しい) ID を持つ HomeTimeline をリクエストする |
|
50 |
*/ |
|
51 |
private long mStatusSinceId = 1L; |
|
52 |
|
|
53 |
private boolean mIsUpdatedStatusSinceId = false; |
|
54 |
|
|
55 |
private RequestHomeTimelineTimerTask(Twitter twitter, Talker talker) { |
|
56 |
mTwitter = twitter; |
|
57 |
mTalker = talker; |
|
58 |
} |
|
59 |
|
|
60 |
/** |
|
61 |
* The action to be performed by this timer task. |
|
62 |
*/ |
|
63 |
@Override |
|
64 |
public void run() { |
|
65 |
try { |
|
66 |
// mStatusSinceId が未更新ならば、 Status を 1 つだけ取得する |
|
67 |
int count = mIsUpdatedStatusSinceId ? HOME_TIMELINE_COUNT_MAX : HOME_TIMELINE_COUNT_MIN; |
|
68 |
Paging paging = new Paging(1, count, mStatusSinceId); |
|
69 |
ResponseList<Status> statusResponseList = mTwitter.getHomeTimeline(paging); |
|
70 |
|
|
71 |
if (statusResponseList.isEmpty()) { |
|
72 |
return; |
|
73 |
} |
|
74 |
|
|
75 |
// mStatusSinceId を、取得した最新の ID に更新する |
|
76 |
mStatusSinceId = statusResponseList.get(0).getId(); |
|
77 |
mIsUpdatedStatusSinceId = true; |
|
78 |
|
|
79 |
// Status が古い順になるよう、 statusResponseList を逆順に並び替える |
|
80 |
Collections.reverse(statusResponseList); |
|
81 |
|
|
82 |
for (Status status : statusResponseList) { |
|
83 |
onStatus(status); |
|
84 |
} |
|
85 |
|
|
86 |
} catch (TwitterException e) { |
|
87 |
e.printStackTrace(); |
|
88 |
} |
|
89 |
} |
|
90 |
|
|
91 |
private void onStatus(final Status status) { |
|
92 |
if (!LANG_JA.equalsIgnoreCase(status.getLang())) { |
|
93 |
return; |
|
94 |
} |
|
95 |
|
|
96 |
final StringBuffer buffer = new StringBuffer(); |
|
97 |
|
|
98 |
if (status.isRetweet()) { |
|
99 |
Status retweetedStatus = status.getRetweetedStatus(); |
|
100 |
buffer.append(getUserNameWithoutContext(status.getUser().getName()) + "さんがリツイート。"); |
|
101 |
buffer.append(getUserNameWithoutContext(retweetedStatus.getUser().getName()) + "さんから、"); |
|
102 |
buffer.append(retweetedStatus.getText()); |
|
103 |
} else { |
|
104 |
buffer.append(getUserNameWithoutContext(status.getUser().getName()) + "さんから、"); |
|
105 |
buffer.append(status.getText()); |
|
106 |
} |
|
107 |
|
|
108 |
mTalker.talkAsync(UrlUtil.convURLEmpty(buffer).replaceAll("\n", "。"), mYukkuriVoice); |
|
109 |
|
|
110 |
// 読み上げは、霊夢と魔理沙が交互に行なう |
|
111 |
if (mYukkuriVoice == Talker.YukkuriVoice.REIMU) { |
|
112 |
mYukkuriVoice = Talker.YukkuriVoice.MARISA; |
|
113 |
} else { |
|
114 |
mYukkuriVoice = Talker.YukkuriVoice.REIMU; |
|
115 |
} |
|
116 |
} |
|
117 |
} |
|
5 |
/* package */ void start(); |
|
118 | 6 |
} |
src/com/mizo0203/timeline/talker/TwitterTimelineTalker.java | ||
---|---|---|
1 |
package com.mizo0203.timeline.talker; |
|
2 |
|
|
3 |
import com.mizo0203.timeline.talker.util.DisplayNameUtil; |
|
4 |
import com.mizo0203.timeline.talker.util.UrlUtil; |
|
5 |
import org.jetbrains.annotations.NotNull; |
|
6 |
import twitter4j.*; |
|
7 |
import twitter4j.conf.Configuration; |
|
8 |
|
|
9 |
import java.util.Collections; |
|
10 |
import java.util.Locale; |
|
11 |
import java.util.Timer; |
|
12 |
import java.util.TimerTask; |
|
13 |
import java.util.concurrent.TimeUnit; |
|
14 |
|
|
15 |
public class TwitterTimelineTalker implements TimelineTalker { |
|
16 |
|
|
17 |
/** ISO 639 言語コード - 日本語 (ja) */ |
|
18 |
private static final String LANG_JA = Locale.JAPAN.getLanguage(); |
|
19 |
|
|
20 |
@NotNull private final RequestHomeTimelineTimerTask mRequestHomeTimelineTimerTask; |
|
21 |
|
|
22 |
public TwitterTimelineTalker(@NotNull Configuration configuration, Talker talker) { |
|
23 |
Twitter twitter = new TwitterFactory(configuration).getInstance(); |
|
24 |
mRequestHomeTimelineTimerTask = new RequestHomeTimelineTimerTask(twitter, talker); |
|
25 |
} |
|
26 |
|
|
27 |
@Override |
|
28 |
public void start() { |
|
29 |
new Timer().schedule(mRequestHomeTimelineTimerTask, 0L, TimeUnit.MINUTES.toMillis(1)); |
|
30 |
} |
|
31 |
|
|
32 |
private static class RequestHomeTimelineTimerTask extends TimerTask { |
|
33 |
|
|
34 |
private static final int HOME_TIMELINE_COUNT_MAX = 200; |
|
35 |
private static final int HOME_TIMELINE_COUNT_MIN = 1; |
|
36 |
|
|
37 |
private final Twitter mTwitter; |
|
38 |
private final Talker mTalker; |
|
39 |
|
|
40 |
/** mStatusSinceId より大きい(つまり、より新しい) ID を持つ HomeTimeline をリクエストする */ |
|
41 |
private long mStatusSinceId = 1L; |
|
42 |
|
|
43 |
private boolean mIsUpdatedStatusSinceId = false; |
|
44 |
|
|
45 |
private RequestHomeTimelineTimerTask(Twitter twitter, Talker talker) { |
|
46 |
mTwitter = twitter; |
|
47 |
mTalker = talker; |
|
48 |
} |
|
49 |
|
|
50 |
/** The action to be performed by this timer task. */ |
|
51 |
@Override |
|
52 |
public void run() { |
|
53 |
try { |
|
54 |
// mStatusSinceId が未更新ならば、 Status を 1 つだけ取得する |
|
55 |
int count = mIsUpdatedStatusSinceId ? HOME_TIMELINE_COUNT_MAX : HOME_TIMELINE_COUNT_MIN; |
|
56 |
Paging paging = new Paging(1, count, mStatusSinceId); |
|
57 |
ResponseList<Status> statusResponseList = mTwitter.getHomeTimeline(paging); |
|
58 |
|
|
59 |
if (statusResponseList.isEmpty()) { |
|
60 |
return; |
|
61 |
} |
|
62 |
|
|
63 |
// mStatusSinceId を、取得した最新の ID に更新する |
|
64 |
mStatusSinceId = statusResponseList.get(0).getId(); |
|
65 |
mIsUpdatedStatusSinceId = true; |
|
66 |
|
|
67 |
// Status が古い順になるよう、 statusResponseList を逆順に並び替える |
|
68 |
Collections.reverse(statusResponseList); |
|
69 |
|
|
70 |
for (Status status : statusResponseList) { |
|
71 |
onStatus(status); |
|
72 |
} |
|
73 |
|
|
74 |
} catch (TwitterException e) { |
|
75 |
e.printStackTrace(); |
|
76 |
} |
|
77 |
} |
|
78 |
|
|
79 |
private void onStatus(final Status status) { |
|
80 |
if (!LANG_JA.equalsIgnoreCase(status.getLang())) { |
|
81 |
return; |
|
82 |
} |
|
83 |
|
|
84 |
final StringBuffer buffer = new StringBuffer(); |
|
85 |
|
|
86 |
if (status.isRetweet()) { |
|
87 |
Status retweetedStatus = status.getRetweetedStatus(); |
|
88 |
buffer |
|
89 |
.append(DisplayNameUtil.removeContext(status.getUser().getName())) |
|
90 |
.append("さんがリツイート。"); |
|
91 |
buffer |
|
92 |
.append(DisplayNameUtil.removeContext(retweetedStatus.getUser().getName())) |
|
93 |
.append("さんから、"); |
|
94 |
buffer.append(retweetedStatus.getText()); |
|
95 |
} else { |
|
96 |
buffer.append(DisplayNameUtil.removeContext(status.getUser().getName())).append("さんから、"); |
|
97 |
buffer.append(status.getText()); |
|
98 |
} |
|
99 |
|
|
100 |
mTalker.talkAlternatelyAsync(UrlUtil.convURLEmpty(buffer).replaceAll("\n", "。")); |
|
101 |
} |
|
102 |
} |
|
103 |
} |
src/com/mizo0203/timeline/talker/UrlUtil.java | ||
---|---|---|
1 |
package com.mizo0203.timeline.talker; |
|
2 |
|
|
3 |
import java.util.regex.Matcher; |
|
4 |
import java.util.regex.Pattern; |
|
5 |
|
|
6 |
/** |
|
7 |
* http://chat-messenger.net/blog-entry-40.html |
|
8 |
*/ |
|
9 |
public class UrlUtil { |
|
10 |
/** URLを抽出するための正規表現パターン */ |
|
11 |
private static final Pattern convURLLinkPtn = Pattern.compile( |
|
12 |
"(http://|https://){1}[\\w\\.\\-/:\\#\\?\\=\\&\\;\\%\\~\\+]+", Pattern.CASE_INSENSITIVE); |
|
13 |
|
|
14 |
/** |
|
15 |
* 指定された文字列内のURLを、正規表現を使用し、 空文字列に変換する。 |
|
16 |
* |
|
17 |
* @param str 指定の文字列。 |
|
18 |
* @return リンクに変換された文字列。 |
|
19 |
*/ |
|
20 |
public static String convURLEmpty(CharSequence str) { |
|
21 |
Matcher matcher = convURLLinkPtn.matcher(str); |
|
22 |
return matcher.replaceAll(""); |
|
23 |
} |
|
24 |
} |
src/com/mizo0203/timeline/talker/util/DisplayNameUtil.java | ||
---|---|---|
1 |
package com.mizo0203.timeline.talker.util; |
|
2 |
|
|
3 |
import org.jetbrains.annotations.NotNull; |
|
4 |
|
|
5 |
import java.util.regex.Matcher; |
|
6 |
import java.util.regex.Pattern; |
|
7 |
|
|
8 |
public class DisplayNameUtil { |
|
9 |
|
|
10 |
public static String removeContext(@NotNull String name) { |
|
11 |
Pattern p = Pattern.compile("([^@@]+).+"); |
|
12 |
Matcher m = p.matcher(name); |
|
13 |
return m.replaceFirst("$1"); |
|
14 |
} |
|
15 |
} |
src/com/mizo0203/timeline/talker/util/HTMLParser.java | ||
---|---|---|
1 |
package com.mizo0203.timeline.talker.util; |
|
2 |
|
|
3 |
import org.apache.commons.io.IOUtils; |
|
4 |
import org.jetbrains.annotations.NotNull; |
|
5 |
|
|
6 |
import javax.swing.text.html.HTMLEditorKit; |
|
7 |
import javax.swing.text.html.parser.ParserDelegator; |
|
8 |
import java.io.IOException; |
|
9 |
import java.io.InputStreamReader; |
|
10 |
import java.nio.charset.Charset; |
|
11 |
|
|
12 |
public class HTMLParser { |
|
13 |
|
|
14 |
@NotNull |
|
15 |
public String parse(@NotNull String html, @NotNull Charset encoding, boolean ignoreCharSet) |
|
16 |
throws IOException { |
|
17 |
try (InputStreamReader r = |
|
18 |
new InputStreamReader(IOUtils.toInputStream(html, encoding), encoding)) { |
|
19 |
HTMLParserCallback hp = new HTMLParserCallback(); |
|
20 |
ParserDelegator parser = new ParserDelegator(); |
|
21 |
parser.parse(r, hp, ignoreCharSet); |
|
22 |
return hp.getText(); |
|
23 |
} |
|
24 |
} |
|
25 |
|
|
26 |
/** |
|
27 |
* http://www.my-notebook.net/736a69e0-820c-423b-9047-a02b8a9eefb1.html |
|
28 |
* |
|
29 |
* <p>HTMLParser.java |
|
30 |
*/ |
|
31 |
private static class HTMLParserCallback extends HTMLEditorKit.ParserCallback { |
|
32 |
private final StringBuffer sb = new StringBuffer(); |
|
33 |
|
|
34 |
private String getText() { |
|
35 |
return sb.toString(); |
|
36 |
} |
|
37 |
|
|
38 |
@Override |
|
39 |
public void handleText(@NotNull char[] data, int pos) { |
|
40 |
sb.append(new String(data)); |
|
41 |
sb.append(System.getProperty("line.separator")); |
|
42 |
} |
|
43 |
} |
|
44 |
} |
src/com/mizo0203/timeline/talker/util/RuntimeUtil.java | ||
---|---|---|
1 |
package com.mizo0203.timeline.talker.util; |
|
2 |
|
|
3 |
import org.jetbrains.annotations.NotNull; |
|
4 |
|
|
5 |
import java.io.IOException; |
|
6 |
|
|
7 |
public class RuntimeUtil { |
|
8 |
|
|
9 |
public static void execute(String[] cmdarray) { |
|
10 |
try { |
|
11 |
Process process = Runtime.getRuntime().exec(cmdarray); |
|
12 |
process.waitFor(); |
|
13 |
process.destroy(); |
|
14 |
} catch (@NotNull IOException | InterruptedException e) { |
|
15 |
e.printStackTrace(); |
|
16 |
} |
|
17 |
} |
|
18 |
} |
src/com/mizo0203/timeline/talker/util/UrlUtil.java | ||
---|---|---|
1 |
package com.mizo0203.timeline.talker.util; |
|
2 |
|
|
3 |
import org.jetbrains.annotations.NotNull; |
|
4 |
|
|
5 |
import java.util.regex.Matcher; |
|
6 |
import java.util.regex.Pattern; |
|
7 |
|
|
8 |
/** http://chat-messenger.net/blog-entry-40.html */ |
|
9 |
public class UrlUtil { |
|
10 |
/** URLを抽出するための正規表現パターン */ |
|
11 |
@SuppressWarnings("Annotator") |
|
12 |
private static final Pattern convURLLinkPtn = |
|
13 |
Pattern.compile( |
|
14 |
"(http://|https://){1}[\\w\\.\\-/:\\#\\?\\=\\&\\;\\%\\~\\+]+", Pattern.CASE_INSENSITIVE); |
|
15 |
|
|
16 |
/** |
|
17 |
* 指定された文字列内のURLを、正規表現を使用し、 空文字列に変換する。 |
|
18 |
* |
|
19 |
* @param str 指定の文字列。 |
|
20 |
* @return リンクに変換された文字列。 |
|
21 |
*/ |
|
22 |
public static String convURLEmpty(@NotNull CharSequence str) { |
|
23 |
Matcher matcher = convURLLinkPtn.matcher(str); |
|
24 |
return matcher.replaceAll(""); |
|
25 |
} |
|
26 |
} |
他の形式にエクスポート: Unified diff
Add feature Mastodon's timeline talking. fix #422 @16.0h