Last active 1630415123

IRC Single Message Bot. Sends a single IRC message to a channel, then quits. Useful for job notifications maybe

Steven Smith revised this gist 1428648720. Go to revision

1 file changed, 0 insertions, 0 deletions

singlemessagebot.py renamed to __init__.py

File renamed without changes

Steven Smith revised this gist 1426795840. Go to revision

1 file changed, 245 insertions

singlemessagebot.py(file created)

@@ -0,0 +1,245 @@
1 + from irc.bot import ServerSpec
2 + import irc
3 + import sys
4 + import json
5 +
6 + try:
7 + with open('smbotconfig.json') as f:
8 + conf = json.load(f)
9 + except:
10 + print "Error in config or no config found, returning to defaults. Please edit smbotconfig.json (http://jsonlint.com/) and restart the bot"
11 + with open('smbotconfig.json', 'w') as f:
12 + f.write(json.dumps(dict(channel="#blha303",
13 + nickname="SingleMessageTest",
14 + server="irc.esper.net") ) )
15 + sys.exit(1)
16 +
17 + class SingleMessageClient(object):
18 + reactor_class = irc.client.Reactor
19 +
20 + def __init__(self):
21 + self.reactor = self.reactor_class()
22 + self.connection = self.reactor.server()
23 + self.dcc_connections = []
24 + self.reactor.add_global_handler("all_events", self._dispatcher, -10)
25 + self.reactor.add_global_handler("dcc_disconnect",
26 + self._dcc_disconnect, -10)
27 +
28 + def _dispatcher(self, connection, event):
29 + """
30 + Dispatch events to on_<event.type> method, if present.
31 + """
32 + do_nothing = lambda c, e: None
33 + method = getattr(self, "on_" + event.type, do_nothing)
34 + method(connection, event)
35 +
36 + def _dcc_disconnect(self, c, e):
37 + self.dcc_connections.remove(c)
38 +
39 + def connect(self, *args, **kwargs):
40 + """Connect using the underlying connection"""
41 + self.connection.connect(*args, **kwargs)
42 +
43 + def dcc_connect(self, address, port, dcctype="chat"):
44 + """Connect to a DCC peer.
45 +
46 + Arguments:
47 +
48 + address -- IP address of the peer.
49 +
50 + port -- Port to connect to.
51 +
52 + Returns a DCCConnection instance.
53 + """
54 + dcc = self.reactor.dcc(dcctype)
55 + self.dcc_connections.append(dcc)
56 + dcc.connect(address, port)
57 + return dcc
58 +
59 + def dcc_listen(self, dcctype="chat"):
60 + """Listen for connections from a DCC peer.
61 +
62 + Returns a DCCConnection instance.
63 + """
64 + dcc = self.reactor.dcc(dcctype)
65 + self.dcc_connections.append(dcc)
66 + dcc.listen()
67 + return dcc
68 +
69 + def start(self):
70 + """Start the IRC client."""
71 + while 1:
72 + if not self.connection.is_connected():
73 + break
74 + self.reactor.process_once(0.2)
75 +
76 + class SingleMessageBot(SingleMessageClient):
77 + def __init__(self, msg, channel=conf.get('channel'),
78 + nickname=conf.get('nickname'),
79 + realname=conf.get('realname', conf.get('nickname', None)),
80 + server=conf.get('server'),
81 + port=conf.get('port', 6667),
82 + password=conf.get('password', None),
83 + **connect_params):
84 + super(SingleMessageBot, self).__init__()
85 + self.__connect_params = connect_params
86 + self.channel = channel
87 + self.server = ServerSpec(server, port, password)
88 + self.msg = msg
89 + self._nickname = nickname
90 + self._realname = realname
91 + for i in ["join", "kick", "mode",
92 + "namreply", "nick", "part", "quit"]:
93 + self.connection.add_global_handler(i, getattr(self, "_on_" + i),
94 + -20)
95 +
96 + def _connect(self):
97 + """
98 + Establish a connection to the server at the front of the server_list.
99 + """
100 + try:
101 + self.connect(self.server.host, self.server.port, self._nickname,
102 + self.server.password, ircname=self._realname,
103 + **self.__connect_params)
104 + except irc.client.ServerConnectionError:
105 + pass
106 +
107 + def _on_join(self, c, e):
108 + ch = e.target
109 + nick = e.source.nick
110 + if nick == c.get_nickname():
111 + self.channels[ch] = Channel()
112 + self.channels[ch].add_user(nick)
113 +
114 + def _on_kick(self, c, e):
115 + nick = e.arguments[0]
116 + channel = e.target
117 +
118 + if nick == c.get_nickname():
119 + del self.channels[channel]
120 + else:
121 + self.channels[channel].remove_user(nick)
122 +
123 + def _on_mode(self, c, e):
124 + modes = irc.modes.parse_channel_modes(" ".join(e.arguments))
125 + t = e.target
126 + if irc.client.is_channel(t):
127 + ch = self.channels[t]
128 + for mode in modes:
129 + if mode[0] == "+":
130 + f = ch.set_mode
131 + else:
132 + f = ch.clear_mode
133 + f(mode[1], mode[2])
134 + else:
135 + # Mode on self... XXX
136 + pass
137 +
138 + def _on_namreply(self, c, e):
139 + """
140 + e.arguments[0] == "@" for secret channels,
141 + "*" for private channels,
142 + "=" for others (public channels)
143 + e.arguments[1] == channel
144 + e.arguments[2] == nick list
145 + """
146 +
147 + ch_type, channel, nick_list = e.arguments
148 +
149 + if channel == '*':
150 + # User is not in any visible channel
151 + # http://tools.ietf.org/html/rfc2812#section-3.2.5
152 + return
153 +
154 + for nick in nick_list.split():
155 + nick_modes = []
156 +
157 + if nick[0] in self.connection.features.prefix:
158 + nick_modes.append(self.connection.features.prefix[nick[0]])
159 + nick = nick[1:]
160 +
161 + for mode in nick_modes:
162 + self.channels[channel].set_mode(mode, nick)
163 +
164 + self.channels[channel].add_user(nick)
165 +
166 + def _on_nick(self, c, e):
167 + before = e.source.nick
168 + after = e.target
169 + for ch in self.channels.values():
170 + if ch.has_user(before):
171 + ch.change_nick(before, after)
172 +
173 + def _on_part(self, c, e):
174 + nick = e.source.nick
175 + channel = e.target
176 +
177 + if nick == c.get_nickname():
178 + del self.channels[channel]
179 + else:
180 + self.channels[channel].remove_user(nick)
181 +
182 + def _on_quit(self, c, e):
183 + nick = e.source.nick
184 + for ch in self.channels.values():
185 + if ch.has_user(nick):
186 + ch.remove_user(nick)
187 +
188 + def die(self, msg="Bye, cruel world!"):
189 + self.connection.disconnect(msg)
190 +
191 + def get_version(self):
192 + """Returns the bot version.
193 +
194 + Used when answering a CTCP VERSION request.
195 + """
196 + return "Python irc.bot ({version})".format(
197 + version=irc.client.VERSION_STRING)
198 +
199 + def jump_server(self, msg="Changing servers"):
200 + """Connect to a new server, possibly disconnecting from the current.
201 +
202 + The bot will skip to next server in the server_list each time
203 + jump_server is called.
204 + """
205 + if self.connection.is_connected():
206 + self.connection.disconnect(msg)
207 +
208 + self.server_list.append(self.server_list.pop(0))
209 + self._connect()
210 +
211 + def on_ctcp(self, c, e):
212 + """Default handler for ctcp events.
213 +
214 + Replies to VERSION and PING requests and relays DCC requests
215 + to the on_dccchat method.
216 + """
217 + nick = e.source.nick
218 + if e.arguments[0] == "VERSION":
219 + c.ctcp_reply(nick, "VERSION " + self.get_version())
220 + elif e.arguments[0] == "PING":
221 + if len(e.arguments) > 1:
222 + c.ctcp_reply(nick, "PING " + e.arguments[1])
223 + elif e.arguments[0] == "DCC" and e.arguments[1].split(" ", 1)[0] == "CHAT":
224 + self.on_dccchat(c, e)
225 +
226 + def on_dccchat(self, c, e):
227 + pass
228 +
229 + def start(self):
230 + """Start the bot."""
231 + self._connect()
232 + super(SingleMessageBot, self).start()
233 +
234 + def on_welcome(self, c, e):
235 + c.join(self.channel)
236 + c.privmsg(self.channel, self.msg)
237 + c.close()
238 +
239 + def main(msg):
240 + b = SingleMessageBot(msg)
241 + b.start()
242 + return 0
243 +
244 + if __name__ == "__main__":
245 + sys.exit(main(" ".join(sys.argv[1:]) if len(sys.argv) > 1 else "This is a test of SingleMessageBot!"))
Newer Older