是否听说过程序员鼓励师,不久前出了一款vscode的插件rainbow-fart,可以在写代码的时候,匹配到特定关键词就疯狂的拍你马屁。
vscode的下载尝试过,但是作为日常将IDEA作为主力生产工具的同学来说,如何体验呢? 于是假期花了一点时间,写了一个idea版本的插件idea-rainbow-fart。
安装方法
先到下载最新的插件。关注公众号Java精选,扫描下方二维码,回复彩虹屁,即可获取下载文件。直接放到对应目录即可应用,试一试被疯狂拍你马屁的插件吧!!!
下载rainbow-fart-1.0-SNAPSHOT.zip,然后打开Idea的插件目录,比如笔者的目录是C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.4\plugins
将rainbow-fart-1.0-SNAPSHOT.zip解压到plugins目录,如图所示:
解压
然后重启IDEA即可。
使用内置语音包
打开设置:
将voice package type设置为builtin
可以选择内置语音包,共三个,一个官方的中文和英文,一个tts合成的(玲姐姐)
使用第三方语音包
将voice package type设置为custom
可以到 https://github.com/topics/vscode-rainbow-fart 查找语音包。
点击确定生效。
使用TTS(推荐)
本插件特色功能,支持自定义关键词和文本,鼠标点击表格可以修改关键词和回复语,修改时enter回车换行,一行代表一个
TTS 使用科大讯飞提供的流式API。
原理
没啥原理,就是一款简单的idea插件,对没写过插件的我来说,需要先看下官方文档,基本上看下面这一篇就OK:
https://www.jetbrains.org/intellij/sdk/docs/basics/getting_started.html
读取语音包
先来看下语音包的设计:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
{
"name"
:
"KugimiyaRie"
,
"display-name"
:
"KugimiyaRie 钉宫理惠 (Japanese)"
,
"avatar"
:
"louise.png"
,
"avatar-dark"
:
"shana.png"
,
"version"
:
"0.0.1"
,
"description"
:
"傲娇钉宫,鞭写鞭骂"
,
"languages"
: [
"javascript"
],
"author"
:
"zthxxx"
,
"gender"
:
"female"
,
"locale"
:
"jp"
,
"contributes"
: [
{
"keywords"
: [
"function"
,
"=>"
],
"voices"
: [
"function_01.mp3"
,
"function_02.mp3"
,
"function_03.mp3"
]
},
...
]
}
|
对Java来说,定义两个bean类,解析json即可:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
/**
* 加载配置
*/
public
static
void
loadConfig() {
try
{
//
FartSettings settings = FartSettings.getInstance();
if
(!settings.isEnable()) {
return
;
}
String json = readVoicePackageJson(
"manifest.json"
);
Gson gson =
new
Gson();
Manifest manifest = gson.fromJson(json, Manifest.
class
);
// load contributes.json
if
(manifest.getContributes() ==
null
) {
String contributesText = readVoicePackageJson(
"contributes.json"
);
Manifest contributes = gson.fromJson(contributesText, Manifest.
class
);
if
(contributes.getContributes() !=
null
) {
manifest.setContributes(contributes.getContributes());
}
}
Context.init(manifest);
}
catch
(IOException e) {
}
}
|
监控用户输入
自定义一个Handler类继承TypedActionHandlerBase即可,需要实现的方法原型是:
1
|
public
void
execute(
@NotNull
Editor editor,
char
charTyped,
@NotNull
DataContext dataContext)
|
chartTyped就是输入的字符,我们可以简单粗暴的将这些组合到一起即可,用一个list缓存,然后将拼接后的字符串匹配关键词。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
private
List<String> candidates =
new
ArrayList<>();
@Override
public
void
execute(
@NotNull
Editor editor,
char
charTyped,
@NotNull
DataContext dataContext) {
candidates.add(String.valueOf(charTyped));
String str = StringUtils.join(candidates,
""
);
try
{
List<String> voices = Context.getCandidate(str);
if
(!voices.isEmpty()) {
Context.play(voices);
candidates.clear();
}
}
catch
(Exception e){
// TODO
candidates.clear();
}
if
(
this
.myOriginalHandler !=
null
) {
this
.myOriginalHandler.execute(editor, charTyped, dataContext);
}
}
|
匹配关键词更简单,将读取出来的json,放到hashmap中,然后遍历map,如果包含关键词就作为语音候选:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public
static
List<String> getCandidate(String inputHistory) {
final
List<String> candidate =
new
ArrayList<>();
FartSettings settings = FartSettings.getInstance();
if
(!settings.isEnable()) {
return
candidate;
}
if
(keyword2Voices !=
null
) {
keyword2Voices.forEach((keyword, voices) -> {
if
(inputHistory.contains(keyword)) {
candidate.addAll(voices);
}
});
}
if
(candidate.isEmpty()) {
candidate.addAll(findSpecialKeyword(inputHistory));
}
return
candidate;
}
|
如果找到候选,就播放。
播放
为了防止同时播放多个语音,我们用一个单线程线程池来搞定。播放器使用javazoom.jl.player.Player
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
/**
* play in a single thread pool
*/
static
ExecutorService playerTheadPool;
static
{
ThreadFactory playerFactory =
new
ThreadFactoryBuilder()
.setNameFormat(
"player-pool-%d"
).build();
playerTheadPool =
new
ThreadPoolExecutor(
1
,
1
,
0L, TimeUnit.MILLISECONDS,
new
LinkedBlockingQueue<>(
1024
), playerFactory,
new
ThreadPoolExecutor.AbortPolicy());
}
public
static
void
play(List<String> voices) {
FartSettings settings = FartSettings.getInstance();
if
(!settings.isEnable()) {
return
;
}
// play in single thread
playerTheadPool.submit(() -> {
String file = voices.get(
new
Random().nextInt() % voices.size());
try
{
InputStream inputStream =
null
;
if
(StringUtils.isEmpty(settings.getCustomVoicePackage())) {
inputStream = Context.
class
.getResourceAsStream(
"/build-in-voice-chinese/"
+ file);
}
else
{
File mp3File = Paths.get(settings.getCustomVoicePackage(), file).toFile();
if
(mp3File.exists()) {
try
{
inputStream =
new
FileInputStream(mp3File);
}
catch
(FileNotFoundException e) {
}
}
else
{
return
;
}
}
if
(inputStream !=
null
) {
Player player =
new
Player(inputStream);
player.play();
player.close();
}
}
catch
(JavaLayerException e) {
}
});
}
|
开源地址: https://github.com/jadepeng/idea-rainbow-fart,欢迎fork代码,贡献代码。
插件参考,感谢原作者的贡献
插件开发参考 https://github.com/izhangzhihao/intellij-rainbow-fart
语音包和插件icon源于 https://github.com/SaekiRaku/vscode-rainbow-fart
作者:jqpeng的技术记事本
来源:https://www.cnblogs.com/xiaoqi/p/idea-rainbow-fart.html