脚本支持是Tigase的一个基本内置API,不需要任何额外的代价就能让所有的组件都自动支持脚本。但它只能访问那些通过你的代码继承到的父类组件变量,所以你需要把你的数据传递给脚本API。这篇文档会教你如何扩展现有的脚本API来访问组件的数据结构。

组件与脚本引擎的集成只需要简单几行代码:



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命令列表的新窗口,其他客户端的展现方式也许不同。


genesis2000跑脚本bitmap文件不存在_脚本语言

命令列表

点击“New command Script”指令会弹出下面的窗口,你需要填写脚本描述和脚本ID。在样例中我们使用Groovy语言,但其实你可以使用更多脚本语言。




genesis2000跑脚本bitmap文件不存在_数据结构_02

垃圾关键字列表脚



如果想要添加更多脚本语言支持,请参考Tigase脚本文档来获得全部细节。对Tigase API而言,所有的语言都是一样的。你需要从窗口的下拉菜单中选择一个合适的语言。如果想使用的脚本语言不在下拉菜单中,那么它没有被正确的安装,所以Tigase无法检测到。

使用Groovy语言来获取当前垃圾关键字列表的代码如下:



defbadw = (java.util.Set)badWords         


          defresult =""         


          for (sinbadw) { result += s +"\n"}         


          returnresult



就像你在脚本中看到的那样,你需要定义一个脚本变量来引用组件中的变量,请使用正确的类型。剩下的事情就是非常简单的纯脚本工作了。执行脚本的结果如下图:




genesis2000跑脚本bitmap文件不存在_脚本语言_03

垃圾关键字脚本执行结果



下面的脚本允许你更新(添加/删除)垃圾关键字:



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的完整代码:



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);         


            }         


                     


          }