-
Notifications
You must be signed in to change notification settings - Fork 2
/
quote.py
119 lines (93 loc) · 3.21 KB
/
quote.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
import re, random, sys, traceback, os
from halibot import HalModule
class Quote(HalModule):
# Configuration defaults
remove_votes = 3
can_remove = True
max_history = 5
def init(self):
self.quotes = []
self.path = self.config.get('quotes-path')
self.remove_votes = self.config.get('remote-votes', self.remove_votes)
self.can_remove = self.config.get('can-remove', self.can_remove)
self.max_history = self.config.get('max-history', self.max_history)
self.log.info('using quotes path: ' + self.path)
if self.path is not None:
try:
with open(self.path, 'r') as f:
self.quotes.extend([x.rstrip() for x in f if x.rstrip()])
except (IOError, OSError):
self.log.error("Couldn't open quotes file: " + traceback.format_exc())
self.rand_quotes = {} # pattern -> shuffled lists
self.rem_vote_map = {} # quote -> set(usernames)
def quote(self, pattern=""):
if pattern not in self.rand_quotes:
if len(pattern) > 0:
try:
expr = re.compile(pattern)
except re.error as e:
return 'Invalid pattern: {}'.format(e)
ls = [q for q in self.quotes if expr.search(q)]
else:
ls = self.quotes[:]
if not ls:
return 'No quotes found with that pattern :('
random.shuffle(ls)
if len(self.rand_quotes) >= self.max_history:
for i in range(len(self.rand_quotes) - self.max_history + 1):
del self.rand_quotes[random.choice(list(self.rand_quotes.keys()))] # XXX LRU?
self.rand_quotes[pattern] = ls
ls = self.rand_quotes[pattern]
quote = ls.pop()
if not ls:
del self.rand_quotes[pattern]
return quote
def quoteadd(self, quote):
self.quotes.append(quote)
if self.path is not None:
try:
with open(self.path, 'a') as f:
print(quote, file=f)
except (IOError, OSError) as e:
return "Couldn't add quote! I'll remember it for now, though :) ({})".format(e)
return 'Added :)'
def quotedel(self, pattern, nick):
if not self.can_remove:
return "I'm sorry, Dave. I'm afraid I can't do that..."
if not pattern:
return 'Please tell me which quote to remove :)'
try:
expr = re.compile(pattern)
except re.error as e:
return 'Invalid pattern: {}'.format(e)
ls = [q for q in self.quotes if expr.search(q)]
if not ls:
return 'No quotes found with that pattern :('
if len(ls) > 1:
return 'That refers to more than one quote--be more specific?'
quote = ls[0]
vote_set = self.rem_vote_map.setdefault(quote, set())
vote_set.add(nick) # TODO prevent vote fraud better
if len(vote_set) < self.remove_votes:
return 'Need {} more votes to approve! :)'.format(
self.remove_votes - len(vote_set),
)
else:
self.quotes.remove(quote)
if self.path is not None:
try:
with open(self.path, 'w') as f:
f.writelines([q + os.linesep for q in self.quotes])
except (IOError, OSError) as e:
return "Couldn't remove quote! I'll forget it for now, though :) ({})".format(e)
return 'Removed :)'
def receive(self, msg):
ls = msg.body.split(' ')
cmd = ls[0]
arg = ' '.join(ls[1:]).strip()
if cmd == '!quote':
self.reply(msg, body=self.quote(pattern=arg))
elif cmd == '!quoteadd':
self.reply(msg, body=self.quoteadd(arg))
elif cmd == '!quotedel':
self.reply(msg, body=self.quotedel(arg, msg.author))