-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
199 lines (153 loc) · 5.32 KB
/
main.py
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
"""Run the bot.
Channel messages, join/part/quit messages and the like are saved to
files under irclogs and printed to stdout. Debugging messages are
printed to stderr and saved in botlog.txt.
"""
import atexit
import collections
import glob
import logging
import os
import time
import curio
from curio import socket, subprocess
import bot
logger = logging.getLogger(__name__)
LOG_LEN = 1000
logs = {} # {channel: deque, ...}
def _format_msg(msg):
return f"[%s] %s\n" % (time.strftime('%d %b %H:%M:%S'), msg)
def _log_filename(channel):
return os.path.join('irclogs', channel + '.txt')
async def log_msg(channel, msg):
try:
log = logs[channel]
except KeyError:
log = collections.deque(maxlen=LOG_LEN)
try:
async with curio.aopen(_log_filename(channel), 'r') as f:
async for line in f:
log.append(line)
except FileNotFoundError:
# We are running for the first time and nothing is logged
# yet.
pass
logs[channel] = log
print(f"({channel})", msg)
log.append(_format_msg(msg))
@atexit.register
def save_logs():
logger.info("saving logs")
try:
os.mkdir('irclogs')
except FileExistsError:
pass
for channel, lines in logs.items():
lines.append(_format_msg("* Shutting down."))
with open(_log_filename(channel), 'w') as f:
f.writelines(lines)
async def termbin(iterable):
"""Paste the content of iterable to termbin and return URL.
The iterable can be asynchronous or synchronous.
"""
try:
logger.info("sending %d lines to termbin", len(iterable))
except TypeError:
# probably a file object or some other iterator
logger.info("sending content of %r to termbin", iterable)
async with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
await sock.connect(('termbin.com', 9999))
if hasattr(type(iterable), '__aiter__'):
async for string in iterable:
# replace is not the best possible way, but at least
# better than failing to termbin anything
await sock.sendall(string.encode('utf-8', errors='replace'))
else:
for string in iterable:
await sock.sendall(string.encode('utf-8', errors='replace'))
byteurl = await sock.recv(1024)
return byteurl.decode('ascii').strip()
@bot.command("!log")
async def termbin_log(event, channel=None):
"""Termbin the log of the channel."""
if channel is None:
channel_given = False
channel = event.target
else:
channel_given = True
lines = logs.get(channel, [])
if lines:
await event.reply(await termbin(lines))
else:
# termbin says "Use netcat." if we send it nothing
msg = f"Nothing is logged from {channel} yet!"
if not channel_given:
msg += (" You can use '!log CHANNEL' to get logs from a "
"specific channel.")
await event.reply(msg)
@bot.command("!src")
async def link_source(event):
"""Send a link to my code :D"""
linkbytes = await subprocess.check_output([
'git', 'config', '--get', 'remote.origin.url'])
link = linkbytes.decode('utf-8').strip()
await event.reply(f"I'm from {link}.")
@bot.command("!wtf")
async def do_wtf(event, acronym):
"""Translate an acronym to English."""
acronym = acronym.upper()
async with curio.aopen('wtf-words.txt', 'r') as f:
async for line in f:
if line.upper().startswith(acronym + ' '):
initialisim, definition = line.split(' ', 1)
definition = definition.lstrip()
await event.reply(f'{initialisim}: {definition}')
return
await event.reply(f"I have no idea what {acronym} means :(")
bot.add_help_command("!help")
@bot.join
@bot.part
@bot.quit
async def info_handler(event):
logmsg = "* {} {}s".format(
event.sender['nick'], event.msg_type.lower())
await log_msg(event.target, logmsg)
@bot.kick
async def kick_handler(event):
logmsg = "{} {}s {} (reason: {})".format(
event.sender['nick'], event.msg_type.lower(),
event.target, event.reason)
await log_msg(event.channel, logmsg)
@bot.privmsg
async def privmsg_handler(event):
await log_msg(event.target, "<%s> %s" % (
event.sender['nick'], event.message))
def greeting():
lines = [
"**************************",
"** Welcome to curiomuz! **",
"**************************",
"\n",
" __ ",
" _ / / ",
" )/ / ",
" / /_ ",
" | | \ ",
" |_/ ",
"\n\n\n",
]
for line in lines:
print(line.center(70).rstrip())
async def main():
greeting()
logging.basicConfig(
filename='botlog.txt', datefmt='%d %b %H:%M:%S', level=logging.DEBUG,
format="[%(asctime)s] %(name)s %(levelname)s: %(message)s")
# unfortunately it's not possible to log to file and stderr with
# just basicConfig :(
logging.getLogger().addHandler(logging.StreamHandler())
bananabot = bot.IrcBot('curiomuz', ['#8banana'])
await bananabot.connect('chat.freenode.net')
await bananabot.mainloop()
if __name__ == '__main__':
curio.run(main())