脚本支持是Tigase的一个基本内置API,不需要任何额外的代价就能让所有的组件都自动支持脚本。但它只能访问那些通过你的代码继承到的父类组件变量,所以你需要把你的数据传递给脚本API。这篇文档会教你如何扩展现有的脚本API来访问组件的数据结构。
组件与脚本引擎的集成只需要简单几行代码:
|
1
2
3
4
5
6
7
8
9
|
privatestaticfinalString BAD_WORDS_VAR ="badWords";
privatestaticfinalString WHITE_LIST_VAR ="whiteList";
@Override
publicvoidinitBindings(Bindings binds) {
super.initBindings(binds);
binds.put(BAD_WORDS_VAR, badWords);
binds.put(WHITE_LIST_VAR, whiteList);
}
|
上面的代码传递了两个组件变量给脚本:“badWords”和“whiteList”,在脚本中变量的名称是一致的。当然也可以使用不同的名称,但一致的名称让事情变得简单和清晰易懂,所以我们在脚本中使用相同的命名。
这样就可以了,实际上,所有的事情都已经完成。在我们过去的版本中,这两个变量是java的字符串数组,所以我们只能够改变她们的元素,却不能通过脚本向数据结构添加或删除元素。这种方式不够“智慧”,为脚本的开发带来了很多限制。为了解决这个问题,我们把保存白名单和垃圾关键字的数据结构调整为“java.util.Set”。这给我们访问数据带来了很多便利也更加灵活。
因为组件已经可以和脚本API进行交互了,接下来我们演示如何通过ad-hoc指令来发送脚本,并对数据结构当中的数据进行添加或删除操作。
如果你使用Psi客户端:首先,在服务发现列表窗口当中双击“test”组件,会弹出一个包含ad-hoc命令列表的新窗口,其他客户端的展现方式也许不同。
命令列表
点击“New command Script”指令会弹出下面的窗口,你需要填写脚本描述和脚本ID。在样例中我们使用Groovy语言,但其实你可以使用更多脚本语言。
垃圾关键字列表脚
如果想要添加更多脚本语言支持,请参考Tigase脚本文档来获得全部细节。对Tigase API而言,所有的语言都是一样的。你需要从窗口的下拉菜单中选择一个合适的语言。如果想使用的脚本语言不在下拉菜单中,那么它没有被正确的安装,所以Tigase无法检测到。
使用Groovy语言来获取当前垃圾关键字列表的代码如下:
|
1
2
3
4
|
defbadw = (java.util.Set)badWords
defresult =""
for (sinbadw) { result += s +"\n"}
returnresult
|
就像你在脚本中看到的那样,你需要定义一个脚本变量来引用组件中的变量,请使用正确的类型。剩下的事情就是非常简单的纯脚本工作了。执行脚本的结果如下图:
垃圾关键字脚本执行结果
下面的脚本允许你更新(添加/删除)垃圾关键字:
|
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
|
importtigase.server.Command
importtigase.server.Packet
defWORDS_LIST_KEY ="words-list"
defOPERATION_KEY ="operation"
defREMOVE ="Remove"
defADD ="Add"
defOPERATIONS = [ADD, REMOVE]
defbadw = (java.util.Set)badWords
defPacket p = (Packet)packet
defwords = Command.getFieldValue(p, WORDS_LIST_KEY)
defoperation = Command.getFieldValue(p, OPERATION_KEY)
if(words ==null) {
// No data to process, let's ask user to provide
// a list of words
defres = (Packet)p.commandResult(Command.DataType.form)
Command.addFieldValue(res, WORDS_LIST_KEY,"","Bad words list")
Command.addFieldValue(res, OPERATION_KEY, ADD,"Operation",
(String[])OPERATIONS, (String[])OPERATIONS)
returnres
}
defwords_list = words.tokenize(",")
if(operation == ADD) {
words_list.each{ badw.add(it.trim()) }
return"Words have been added."
}
if(operation == REMOVE) {
words_list.each{ badw.remove(it.trim()) }
return"Words have been removed."
}
return"Unknown operation: "+ operation
|
学习这两个脚本只是开始。脚本应用的空间是非常广泛,现在我们仅仅为脚本添加了很少的几行代码,未来你可以借助脚本在运行时扩展你的应用,为它添加各种各样的功能;你也可以重新加载脚本,添加/修改或删除你需要的功能。不需要重启服务器,也不需要重新编译代码,更可以使用任何你希望使用的脚本语言。
当然了白名单的操作其实和垃圾关键字的操作是完全一样的,这里不再多讲了。
下面是我们在文章一开始提到的把字符串数组调整为Set的完整代码:
|
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
|
importjava.util.Arrays;
importjava.util.Collections;
importjava.util.Map;
importjava.util.Set;
importjava.util.concurrent.CopyOnWriteArraySet;
importjava.util.logging.Level;
importjava.util.logging.Logger;
importjavax.script.Bindings;
importtigase.server.AbstractMessageReceiver;
importtigase.server.Packet;
importtigase.stats.StatisticsList;
importtigase.util.JIDUtils;
importtigase.xmpp.StanzaType;
publicclassTestComponentextendsAbstractMessageReceiver {
privatestaticfinalLogger log =
Logger.getLogger(TestComponent.class.getName());
privatestaticfinalString BAD_WORDS_KEY ="bad-words";
privatestaticfinalString WHITELIST_KEY ="white-list";
privatestaticfinalString PREPEND_TEXT_KEY ="log-prepend";
privatestaticfinalString SECURE_LOGGING_KEY ="secure-logging";
privatestaticfinalString ABUSE_ADDRESS_KEY ="abuse-address";
privatestaticfinalString NOTIFICATION_FREQ_KEY ="notification-freq";
privatestaticfinalString BAD_WORDS_VAR ="badWords";
privatestaticfinalString WHITE_LIST_VAR ="whiteList";
privatestaticfinalString[] INITIAL_BAD_WORDS = {"word1","word2","word3"};
privatestaticfinalString[] INITIAL_WHITE_LIST = {"[email protected]"};
/**
* 当Set在一个线程当中进行遍历的时候内容有可能被另一个线程修改,我们认为这种修改是非常小并且很少会发生的,因为绝大多数的操作仅仅是遍历
*/
privateSet<String> badWords =newCopyOnWriteArraySet<String>();
/**
* 当Set在一个线程当中进行遍历的时候内容有可能被另一个线程修改,我们认为这种修改是非常小并且很少会发生的,因为绝大多数的操作仅仅是调用contains(...)方法
*/
privateSet<String> whiteList =newConcurrentSkipListSet<String>();
privateString prependText ="Spam detected: ";
privateString abuseAddress ="[email protected]";
privateintnotificationFrequency =10;
privateintdelayCounter =0;
privatebooleansecureLogging =false;
privatelongspamCounter =0;
privatelongtotalSpamCounter =0;
privatelongmessagesCounter =0;
@Override
publicvoidprocessPacket(Packet packet) {
// 这是一个message packet吗?
if("message"== packet.getElemName()) {
updateServiceDiscoveryItem(getName(),"messages",
"Messages processed: ["+ (++messagesCounter) +"]",true);
String from = JIDUtils.getNodeID(packet.getElemFrom());
// 消息的发送者在白名单内吗?
if(!whiteList.contains(from)) {
// 如果ta不在白名单里面,那么检查消息的内容
String body = packet.getElemCData("/message/body");
if(body !=null&& !body.isEmpty()) {
body = body.toLowerCase();
for(String word : badWords) {
if(body.contains(word)) {
log.finest(prependText + packet.toString(secureLogging));
++spamCounter;
updateServiceDiscoveryItem(getName(),"spam","Spam caught: ["+
(++totalSpamCounter) +"]",true);
return;
}
}
}
}
}
// 不是垃圾信息,返回以便做下一步处理
Packet result = packet.swapElemFromTo();
addOutPacket(result);
}
@Override
publicintprocessingThreads() {
returnRuntime.getRuntime().availableProcessors();
}
@Override
publicinthashCodeForPacket(Packet packet) {
if(packet.getElemTo() !=null) {
returnpacket.getElemTo().hashCode();
}
// 程序不应该运行到这里,所有的packet都必须具有一个目的地地址,但是也许垃圾过滤器也许会过滤一些奇怪的地址
if(packet.getElemFrom() !=null) {
returnpacket.getElemFrom().hashCode();
}
// 如果程序真的运行到这一部,就应该好好检查一下到达组件的packet是否正常,然后找到一个更好的计算hashCode方法。
return1;
}
@Override
publicMap<String, Object> getDefaults(Map<String, Object> params) {
Map<String, Object> defs =super.getDefaults(params);
Collections.addAll(badWords, INITIAL_BAD_WORDS);
Collections.addAll(whiteList, INITIAL_WHITE_LIST);
defs.put(BAD_WORDS_KEY, INITIAL_BAD_WORDS);
defs.put(WHITELIST_KEY, INITIAL_WHITE_LIST);
defs.put(PREPEND_TEXT_KEY, prependText);
defs.put(SECURE_LOGGING_KEY, secureLogging);
defs.put(ABUSE_ADDRESS_KEY, abuseAddress);
defs.put(NOTIFICATION_FREQ_KEY, notificationFrequency);
returndefs;
}
@Override
publicvoidsetProperties(Map<String, Object> props) {
super.setProperties(props);
Collections.addAll(badWords, (String[])props.get(BAD_WORDS_KEY));
Collections.addAll(whiteList, (String[])props.get(WHITELIST_KEY));
prependText = (String)props.get(PREPEND_TEXT_KEY);
secureLogging = (Boolean)props.get(SECURE_LOGGING_KEY);
abuseAddress = (String)props.get(ABUSE_ADDRESS_KEY);
notificationFrequency = (Integer)props.get(NOTIFICATION_FREQ_KEY);
updateServiceDiscoveryItem(getName(),null, getDiscoDescription(),
"automation","spam-filtering",true,
"tigase:x:spam-filter","tigase:x:spam-reporting");
}
@Override
publicsynchronizedvoideveryMinute() {
super.everyMinute();
if((++delayCounter) >= notificationFrequency) {
addOutPacket(Packet.getMessage(abuseAddress, getComponentId(),
StanzaType.chat,"Detected spam messages: "+ spamCounter,
"Spam counter",null, newPacketId("spam-")));
delayCounter =0;
spamCounter =0;
}
}
@Override
publicString getDiscoDescription() {
return"Spam filtering";
}
@Override
publicString getDiscoCategoryType() {
return"spam";
}
@Override
publicvoidgetStatistics(StatisticsList list) {
super.getStatistics(list);
list.add(getName(),"Spam messages found", totalSpamCounter,
Level.INFO);
list.add(getName(),"All messages processed", messagesCounter,
Level.FINE);
if(list.checkLevel(Level.FINEST)) {
// 可以把那些非常消耗系统资源的统计数据产生代码写在下面
}
}
@Override
publicvoidinitBindings(Bindings binds) {
super.initBindings(binds);
binds.put(BAD_WORDS_VAR, badWords);
binds.put(WHITE_LIST_VAR, whiteList);
}
}
|
转载于:https://my.oschina.net/greki/blog/209923