Tigase組件第六節 – 腳本支持

腳本支持是Tigase的一個基本內置API,不須要任何額外的代價就能讓全部的組件都自動支持腳本。但它只能訪問那些經過你的代碼繼承到的父類組件變量,因此你須要把你的數據傳遞給腳本API。這篇文檔會教你如何擴展示有的腳本API來訪問組件的數據結構。 java

組件與腳本引擎的集成只須要簡單幾行代碼: 數組

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指令來發送腳本,並對數據結構當中的數據進行添加或刪除操做。 ide

若是你使用Psi客戶端:首先,在服務發現列表窗口當中雙擊「test」組件,會彈出一個包含ad-hoc命令列表的新窗口,其餘客戶端的展示方式也許不一樣。 學習


命令列表 spa

點擊「New command Script」指令會彈出下面的窗口,你須要填寫腳本描述和腳本ID。在樣例中咱們使用Groovy語言,但其實你可使用更多腳本語言。 線程


垃圾關鍵字列表腳 orm

若是想要添加更多腳本語言支持,請參考Tigase腳本文檔來得到所有細節。對Tigase API而言,全部的語言都是同樣的。你須要從窗口的下拉菜單中選擇一個合適的語言。若是想使用的腳本語言不在下拉菜單中,那麼它沒有被正確的安裝,因此Tigase沒法檢測到。 server

使用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 = {"admin@localhost"};
  
  /**
   * 當Set在一個線程當中進行遍歷的時候內容有可能被另外一個線程修改,咱們認爲這種修改是很是小而且不多會發生的,由於絕大多數的操做僅僅是遍歷
   */
  privateSet<String> badWords =newCopyOnWriteArraySet<String>();
  /**
   * 當Set在一個線程當中進行遍歷的時候內容有可能被另外一個線程修改,咱們認爲這種修改是很是小而且不多會發生的,由於絕大多數的操做僅僅是調用contains(...)方法
   */
  privateSet<String> whiteList =newConcurrentSkipListSet<String>();
  privateString prependText ="Spam detected: ";
  privateString abuseAddress ="abuse@locahost";
  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);
  }
  
}
相關文章
相關標籤/搜索