Sat, 27 Jun 2015 22:32:23 +0200
Add plugin for fetching issue info from GitHub
6
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
1 | -- disco.lua |
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
2 | |
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
3 | -- Responds to service discovery queries (XEP-0030), and calculates the entity |
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
4 | -- capabilities hash (XEP-0115). |
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
5 | |
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
6 | -- Fill the bot.disco.info.identities, bot.disco.info.features, and |
16 | 7 | -- bot.disco.items tables with the relevant disco data. It comes pre-populated |
6
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
8 | -- to advertise support for disco#info, disco#items, and entity capabilities, |
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
9 | -- and to identify itself as Riddim. |
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
10 | |
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
11 | -- If you want to advertise a node, add entries to the bot.disco.nodes table |
16 | 12 | -- with the relevant data. The bot.disco.nodes table should have the same |
13 | -- format as bot.disco (without the nodes element). The nodes are NOT | |
6
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
14 | -- automatically added to the base disco items, so you will need to add them |
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
15 | -- yourself. |
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
16 | |
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
17 | -- To property implement Entity Capabilities, you should make sure that you |
16 | 18 | -- send a "c" element within presence stanzas that are sent. The correct "c" |
6
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
19 | -- element can be obtained by calling bot.caps() (or bot:caps()). |
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
20 | |
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
21 | -- Hubert Chathi <hubert@uhoreg.ca> |
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
22 | |
16 | 23 | -- This file is hereby placed in the public domain. Feel free to modify and |
6
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
24 | -- redistribute it at will |
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
25 | |
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
26 | local st = require "util.stanza" |
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
27 | local b64 = require("mime").b64 |
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
28 | local sha1 = require("util.hashes").sha1 |
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
29 | |
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
30 | function riddim.plugins.disco(bot) |
16 | 31 | bot.disco = {} |
32 | bot.disco.info = {} | |
33 | bot.disco.info.identities = { | |
34 | {category = 'client', type='bot', name='Riddim'}, | |
35 | } | |
36 | bot.disco.info.features = { | |
37 | {var = 'http://jabber.org/protocol/caps'}, | |
38 | {var = 'http://jabber.org/protocol/disco#info'}, | |
39 | {var = 'http://jabber.org/protocol/disco#items'}, | |
40 | } | |
41 | bot.disco.items = {} | |
42 | bot.disco.nodes = {} | |
43 | ||
44 | bot.caps = {} | |
45 | bot.caps.node = 'http://code.matthewwild.co.uk/riddim/' | |
6
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
46 | |
16 | 47 | local function cmp_identity(item1, item2) |
48 | if item1.category < item2.category then | |
49 | return true; | |
50 | elseif item2.category < item1.category then | |
51 | return false; | |
52 | end | |
53 | if item1.type < item2.type then | |
54 | return true; | |
55 | elseif item2.type < item1.type then | |
56 | return false; | |
57 | end | |
58 | if (not item1['xml:lang'] and item2['xml:lang']) or | |
59 | (item2['xml:lang'] and item1['xml:lang'] < item2['xml:lang']) then | |
60 | return true | |
61 | end | |
62 | return false | |
63 | end | |
6
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
64 | |
16 | 65 | local function cmp_feature(item1, item2) |
66 | return item1.var < item2.var | |
67 | end | |
6
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
68 | |
16 | 69 | local function calculate_hash() |
70 | table.sort(bot.disco.info.identities, cmp_identity) | |
71 | table.sort(bot.disco.info.features, cmp_feature) | |
72 | local S = '' | |
73 | for key,identity in pairs(bot.disco.info.identities) do | |
74 | S = S .. string.format( | |
75 | '%s/%s/%s/%s', identity.category, identity.type, | |
76 | identity['xml:lang'] or '', identity.name or '' | |
77 | ) .. '<' | |
78 | end | |
79 | for key,feature in pairs(bot.disco.info.features) do | |
80 | S = S .. feature.var .. '<' | |
81 | end | |
82 | -- FIXME: make sure S is utf8-encoded | |
83 | return (b64(sha1(S))) | |
84 | end | |
6
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
85 | |
16 | 86 | setmetatable(bot.caps, { |
87 | __call = function (...) -- vararg: allow calling as function or member | |
88 | -- retrieve the c stanza to insert into the | |
89 | -- presence stanza | |
90 | local hash = calculate_hash() | |
91 | return st.stanza('c', { | |
92 | xmlns = 'http://jabber.org/protocol/caps', | |
93 | hash = 'sha-1', | |
94 | node = bot.caps.node, | |
95 | ver = hash | |
96 | }) | |
97 | end | |
98 | }) | |
6
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
99 | |
16 | 100 | bot:hook("iq/http://jabber.org/protocol/disco#info", function (event) |
101 | local stanza = event.stanza | |
102 | if stanza.attr.type == 'get' then | |
103 | local query = stanza:child_with_name('query') | |
104 | if not query then return; end | |
105 | -- figure out what identities/features to send | |
106 | local identities | |
107 | local features | |
108 | if query.attr.node then | |
109 | local hash = calculate_hash() | |
110 | local node = bot.disco.nodes[query.attr.node] | |
111 | if node and node.info then | |
112 | identities = node.info.identities or {} | |
113 | features = node.info.identities or {} | |
114 | elseif query.attr.node == bot.caps.node..'#'..hash then | |
115 | -- matches caps hash, so use the main info | |
116 | identities = bot.disco.info.identities | |
117 | features = bot.disco.info.features | |
118 | else | |
119 | -- unknown node: give an error | |
120 | local response = st.stanza('iq',{ | |
121 | to = stanza.attr.from, | |
122 | from = stanza.attr.to, | |
123 | id = stanza.attr.id, | |
124 | type = 'error' | |
125 | }) | |
126 | response:tag('query',{xmlns = 'http://jabber.org/protocol/disco#info'}):reset() | |
127 | response:tag('error',{type = 'cancel'}):tag( | |
128 | 'item-not-found',{xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas'} | |
129 | ) | |
130 | bot:send(response) | |
131 | return true | |
132 | end | |
133 | else | |
134 | identities = bot.disco.info.identities | |
135 | features = bot.disco.info.features | |
136 | end | |
137 | -- construct the response | |
138 | local result = st.stanza('query',{ | |
139 | xmlns = 'http://jabber.org/protocol/disco#info', | |
140 | node = query.attr.node | |
141 | }) | |
142 | for key,identity in pairs(identities) do | |
143 | result:tag('identity', identity):reset() | |
144 | end | |
145 | for key,feature in pairs(features) do | |
146 | result:tag('feature', feature):reset() | |
147 | end | |
148 | bot:send(st.stanza('iq',{ | |
149 | to = stanza.attr.from, | |
150 | from = stanza.attr.to, | |
151 | id = stanza.attr.id, | |
152 | type = 'result' | |
153 | }):add_child(result)) | |
6
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
154 | return true |
16 | 155 | end |
156 | end); | |
6
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
157 | |
16 | 158 | bot:hook("iq/http://jabber.org/protocol/disco#items", function (event) |
159 | local stanza = event.stanza | |
160 | if stanza.attr.type == 'get' then | |
161 | local query = stanza:child_with_name('query') | |
162 | if not query then return; end | |
163 | -- figure out what items to send | |
164 | local items | |
165 | if query.attr.node then | |
166 | local node = bot.disco.nodes[query.attr.node] | |
167 | if node then | |
168 | items = node.items or {} | |
169 | else | |
170 | -- unknown node: give an error | |
171 | local response = st.stanza('iq',{ | |
172 | to = stanza.attr.from, | |
173 | from = stanza.attr.to, | |
174 | id = stanza.attr.id, | |
175 | type = 'error' | |
176 | }) | |
177 | response:tag('query',{xmlns = 'http://jabber.org/protocol/disco#items'}):reset() | |
178 | response:tag('error',{type = 'cancel'}):tag( | |
179 | 'item-not-found',{xmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas'} | |
180 | ) | |
181 | bot:send(response) | |
182 | return true | |
183 | end | |
184 | else | |
185 | items = bot.disco.items | |
186 | end | |
187 | -- construct the response | |
188 | local result = st.stanza('query',{ | |
189 | xmlns = 'http://jabber.org/protocol/disco#items', | |
190 | node = query.attr.node | |
191 | }) | |
192 | for key,item in pairs(items) do | |
193 | result:tag('item', item):reset() | |
194 | end | |
195 | bot:send(st.stanza('iq',{ | |
196 | to = stanza.attr.from, | |
197 | from = stanza.attr.to, | |
198 | id = stanza.attr.id, | |
199 | type = 'result' | |
200 | }):add_child(result)) | |
6
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
201 | return true |
16 | 202 | end |
203 | end); | |
6
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
204 | end |
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
205 | |
b0fec41e695b
initial implementation of disco responses (XEP-0030) and entity caps sending (XEP-0115)
Hubert Chathi <hubert@uhoreg.ca>
parents:
diff
changeset
|
206 | -- end of disco.lua |