[f3af614] | 1 | #!/usr/bin/python |
---|
| 2 | |
---|
| 3 | import sys |
---|
| 4 | import bjsonrpc |
---|
| 5 | from bjsonrpc.handlers import BaseHandler |
---|
| 6 | |
---|
| 7 | import random |
---|
| 8 | import re |
---|
| 9 | import socket |
---|
| 10 | import time |
---|
| 11 | |
---|
[2533d10] | 12 | import feedparser |
---|
| 13 | |
---|
[f3af614] | 14 | # List of functions an IM plugin can export. This library will indicate to |
---|
| 15 | # BitlBee which functions are actually implemented so omitted features |
---|
| 16 | # will be disabled, but note that some/many functions are simply mandatory. |
---|
[2533d10] | 17 | # (Currently login/-out, buddy_msg, add/remove_buddy.) |
---|
[f3af614] | 18 | SUPPORTED_FUNCTIONS = [ |
---|
| 19 | 'login', 'keepalive', 'logout', 'buddy_msg', 'set_away', |
---|
| 20 | 'send_typing', 'add_buddy', 'remove_buddy', 'add_permit', |
---|
| 21 | 'add_deny', 'rem_permit', 'rem_deny', 'get_info', 'chat_invite', |
---|
| 22 | 'chat_kick', 'chat_leave', 'chat_msg', 'chat_with', 'chat_join', |
---|
| 23 | 'chat_topic' |
---|
| 24 | ] |
---|
| 25 | |
---|
| 26 | class RpcForwarder(object): |
---|
| 27 | """Tiny object that forwards RPCs from local Python code to BitlBee |
---|
| 28 | with a marginally nicer syntax. This layer could eventually be |
---|
| 29 | used to add basic parameter checking though I don't think that should |
---|
| 30 | be done here.""" |
---|
| 31 | |
---|
| 32 | def __init__(self, methods, target): |
---|
| 33 | for m in methods: |
---|
| 34 | # imc(b)_ prefix is not useful here, chop it. |
---|
| 35 | # (Maybe do this in BitlBee already as well.) |
---|
| 36 | newname = re.sub("^imcb?_", "", m) |
---|
| 37 | self.__setattr__(newname, target.__getattr__(m)) |
---|
| 38 | |
---|
| 39 | class BitlBeeIMPlugin(BaseHandler): |
---|
[c5a7b8d] | 40 | # Protocol name to be used in the BitlBee CLI, etc. |
---|
[f3af614] | 41 | NAME = "rpc-test" |
---|
[c5a7b8d] | 42 | |
---|
| 43 | # See account.h (TODO: Add constants.) |
---|
| 44 | ACCOUNT_FLAGS = 3 |
---|
| 45 | |
---|
| 46 | # Supported away states. If your protocol supports a specific set of |
---|
| 47 | # away states, put them in a list in this variable. |
---|
[2533d10] | 48 | AWAY_STATES = None |
---|
[f3af614] | 49 | |
---|
| 50 | # Filled in during initialisation: |
---|
| 51 | # Version code in hex. So if you need to do comparisions, for example |
---|
| 52 | # check "self.bitlbee_version >= 0x030202" for versions 3.2.2+ |
---|
| 53 | bitlbee_version = None |
---|
| 54 | # Full version string |
---|
| 55 | bitlbee_version_str = None |
---|
[c5a7b8d] | 56 | # Will become an RpcForwarder object to call into BitlBee |
---|
| 57 | bee = None |
---|
[f3af614] | 58 | |
---|
| 59 | @classmethod |
---|
| 60 | def _factory(cls, *args, **kwargs): |
---|
| 61 | def handler_factory(connection): |
---|
| 62 | handler = cls(connection, *args, **kwargs) |
---|
| 63 | return handler |
---|
| 64 | return handler_factory |
---|
| 65 | |
---|
| 66 | #def __init__(self, connection, *args, **kwargs): |
---|
| 67 | # BaseHandler.__init__(self,connection) |
---|
| 68 | |
---|
| 69 | def init(self, bee): |
---|
| 70 | self.bee = RpcForwarder(bee["method_list"], self._conn.call) |
---|
| 71 | self.bitlbee_version = bee["version"] |
---|
| 72 | self.bitlbee_version_str = bee["version_str"] |
---|
[2533d10] | 73 | |
---|
| 74 | self.feeds = {} |
---|
| 75 | |
---|
[f3af614] | 76 | # TODO: See how to call into the module here. |
---|
| 77 | return { |
---|
| 78 | "name": self.NAME, |
---|
| 79 | "method_list": list(set(dir(self)) & set(SUPPORTED_FUNCTIONS)), |
---|
[c5a7b8d] | 80 | "account_flags": self.ACCOUNT_FLAGS, |
---|
| 81 | "away_state_list": self.AWAY_STATES, |
---|
[f3af614] | 82 | "settings": { |
---|
| 83 | "oauth": { |
---|
| 84 | "default": "off", |
---|
[f15553d] | 85 | "type": "bool", |
---|
[f3af614] | 86 | }, |
---|
| 87 | "test": { |
---|
| 88 | "default": "123", |
---|
[f15553d] | 89 | "type": "int", |
---|
[f3af614] | 90 | }, |
---|
[f15553d] | 91 | "stringetje": { |
---|
| 92 | "default": "testje", |
---|
| 93 | "flags": 0x04, |
---|
| 94 | } |
---|
[f3af614] | 95 | }, |
---|
| 96 | } |
---|
| 97 | |
---|
| 98 | def login(self, account): |
---|
| 99 | print "Logging in with username %s and password %s" % (account['user'], account['pass']) |
---|
| 100 | self.bee.log("Blaataap %r" % account) |
---|
| 101 | self.bee.error("HALP!") |
---|
| 102 | self.bee.connected() |
---|
| 103 | return [{1:2,3:4}, {"a":"A", "b":"B"}, 1, 2, True] |
---|
| 104 | |
---|
| 105 | def logout(self): |
---|
| 106 | self.bee.error("Ok bye!") |
---|
| 107 | |
---|
| 108 | def add_buddy(self, handle, group): |
---|
[2533d10] | 109 | self.bee.add_buddy(handle, None) |
---|
| 110 | self.feeds[handle] = { |
---|
| 111 | "url": handle, |
---|
| 112 | "seen": set(), |
---|
| 113 | } |
---|
[c5a7b8d] | 114 | |
---|
[2533d10] | 115 | def buddy_msg(self, handle, msg, flags): |
---|
| 116 | feed = self.feeds[handle] |
---|
| 117 | cmd = re.split(r"\s+", msg) |
---|
| 118 | if cmd[0] == "list": |
---|
| 119 | fp = feedparser.parse(handle) |
---|
| 120 | for art in fp.entries: |
---|
| 121 | line = "%(title)s <%(link)s>" % art |
---|
| 122 | ts = 0 |
---|
| 123 | if "updated_parsed" in art: |
---|
| 124 | ts = int(time.mktime(art.updated_parsed)) |
---|
| 125 | self.bee.buddy_msg(handle, line, 0, ts) |
---|
| 126 | feed["seen"].add(art.id) |
---|
[f15553d] | 127 | |
---|
| 128 | def set_set(self, setting, value): |
---|
| 129 | print "Setting %s changed to %r" % (setting, value) |
---|
[f3af614] | 130 | |
---|
| 131 | def RunPlugin(plugin, debug=True): |
---|
| 132 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
---|
| 133 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
---|
| 134 | sock.bind("/tmp/rpcplugins/test2") |
---|
| 135 | sock.listen(3) |
---|
| 136 | |
---|
| 137 | srv = bjsonrpc.server.Server(sock, plugin._factory()) |
---|
| 138 | |
---|
| 139 | srv.debug_socket(debug) |
---|
| 140 | srv.serve() |
---|
| 141 | |
---|
| 142 | RunPlugin(BitlBeeIMPlugin) |
---|