wantzel.py 7.49 KB
Newer Older
1
# encoding: utf-8
2
"""
Mindiell's avatar
Mindiell committed
3
Bot Wantzel from La Quadrature du Net.
4

Mindiell's avatar
Mindiell committed
5 6
License : AGPLv3
Doc     : https://wiki.laquadrature.net/Wantzel
7 8 9
"""

import re
10 11

from irc import IrcClientFactory
Mindiell's avatar
Mindiell committed
12
from twisted.internet import reactor, task, ssl
13 14

import config
15
from logs import Log
16

17
# bot modules
18
import helpers
19 20
from admin import Admin
from fun import fun
21
from monitor import Monitoring
22
from rp import Rp
23

24 25 26 27 28 29
class Wantzel(object):
    """
    Wantzel bot.
    """
    def __init__(self):
        """
30
        Initialization of all utility objects and bot over IRC.
31
        """
32
        self.admin = Admin()
33
        self.monitor = Monitoring()
34
        self.rp = Rp()
35
        # Connection to IRC
36
        self.irc = IrcClientFactory(config)
37
        self.irc.set_callbacks = self.set_callbacks
Mindiell's avatar
Mindiell committed
38
        reactor.connectSSL(config.server, config.port, self.irc, ssl.ClientContextFactory())
39
        self.test_timer = ""
40
        self.topic = ""
41
        # Loop call
42
        self.loop_started = False
43
        self.loop = task.LoopingCall(self.timer)
44 45 46 47 48

    def timer(self):
        """
        This method launches function regularly (see config.timer).
        """
49
        Log.debug("Timer called")
50
        # Testing timer ?
51 52 53
        if self.test_timer!="":
            self.send_message(self.test_timer, "Timer fonctionnel!")        
            self.test_timer = ""
54 55
        # Tweeting Press reviews f necessary
        self.rp.tweet()
56
        # Update topic based on number of articles waiting in queue if necessary
57
        topic = self.rp.count_articles()
58
        if topic != self.topic:
59 60
            self.irc.client.topic(config.RP_CHANNEL, topic)
        # Tell on channel if a wiki was modified since last time
61
        self.send_message(config.MONITOR_CHANNEL, self.monitor.update())
62
        # Cleaning points of mastering rp
63
        self.rp.clean_master_rp()
64

65
    def set_callbacks(self):
66 67 68
        """
        This method set the methods to call for each callback received from IRC.
        """
69
        # When receiving a message
70
        self.irc.client.privmsg = self.on_privmsg
71 72
        # When topic is modified
        self.irc.client.topicUpdated = self.topic_updated
73

74
    def send_message(self, channel, multiline_message=""):
75 76 77
        """
        Sends a message on specified channel, cutting each line in a new message
        """
78 79
        if isinstance(multiline_message, list):
            multiline_message = "\n".join(multiline_message)
80
        for message in multiline_message.splitlines():
81 82
            self.irc.client.msg(channel, message)

83 84 85 86 87 88 89 90 91 92 93
    def topic_updated(self, user, channel, newTopic):
        """
        Topic has been modified, or bot is coming in the channel.
        If the bot is coming in, we can start its timer's loop
        """
        self.topic = newTopic
        # Can I start the loop ?
        if not self.loop_started:
            self.loop_started = True
            self.loop.start(config.timer)

94 95
    def on_privmsg(self, user, channel, msg):
        """
96 97
        Wantzel can understand a lot of commands. Commands followed by a (*)
        are accessible only to moderators:
98 99 100
        - help
            Returns a message about how to use the bot.
            If a command is passed after help, the message explains how to use
101
            the command.
102
            Bot answers in private in order not to spam channel
103
        - rp(acp) <url>
104
            Add an article in the database with a specific flag
105 106
        - status <url>
            Retrieve some informations about an article in the database
107
        - stats
Mindiell's avatar
Mindiell committed
108
            Show some statistics about the RP
109
        - kill (*)
Mindiell's avatar
Mindiell committed
110
            Kill an article by giving it a score of -100
111
        - admin list (*)
112
            List rights in private
113
        - admin add (*)
114 115 116
            Add one or more new moderator to list
        - admin del (*)
            Delete one or more moderator from list
117
        - admin timer
118
            Relaunch a timer
119 120
        """
        # Cleaning user name
Mindiell's avatar
Mindiell committed
121
        user = re.search("([^!]*)!", user).group(1)
122
        Log.debug("Message received: %s %s %s" % (user, channel, msg))
Mindiell's avatar
Mindiell committed
123
        # Never answer to botself
124
        if user!=config.nickname:
Mindiell's avatar
Mindiell committed
125 126 127 128 129
            # If it's a query, bot should answer to the user as the channel
            if "#" not in channel:
                channel = user
            # Help command, specific
            if "wantzel" in msg and ("help" in msg or "aide" in msg):
130
                self.help(user, channel, msg)
Mindiell's avatar
Mindiell committed
131
            # Find known command
132 133
            command = re.search("[!~](rp[acp]*|status|kill|help|stats|admin)", msg)
            Log.debug("Command: %s" % command)
Mindiell's avatar
Mindiell committed
134
            if command:
135
                Log.debug("group(0): %s" % command.group(0))
Mindiell's avatar
Mindiell committed
136
                command = command.group(1)
137
                Log.debug("Command: %s" % command)
Mindiell's avatar
Mindiell committed
138
                if command.startswith("rp"):
139 140
                    Log.debug("Calling self.rp")
                    self.send_message(channel, self.rp.rp(command, user, channel, msg))
141
                if command.startswith("status"):
142 143
                    Log.debug("Calling self.status")
                    self.send_message(channel, self.rp.status(user, msg))
144
                elif command == "help":
145
                    Log.debug("Calling self.help")
146
                    self.help(user, channel, msg)
147
                elif command == "kill":
148 149
                    Log.debug("Calling self.kill")
                    self.send_message(channel, self.rp.kill(user, msg))
150
                elif command == "stats":
151 152
                    Log.debug("Calling self.stats")
                    self.send_message(channel, self.rp.stats())
153
                elif command == "admin":
154 155 156 157
                    Log.debug("Calling self.admin")
                    result = self.admin.admin(user, msg)
                    if result=="REACTOR":
                        try:
158 159
                            # Re-creating timer
                            self.loop = task.LoopingCall(self.timer)
160
                            self.loop.start(config.timer)
161 162
                        except Exception:
                            pass
Mindiell's avatar
Mindiell committed
163
                    elif result=="TEST_REACTOR":
164
                        self.test_timer = channel
165
                        self.send_message(channel, "Test du timer en cours...")
166 167 168
                    else:
                        self.send_message(channel, result)
            self.send_message(channel, fun(user, channel, msg))
169

170
    def help(self, user, channel, msg):
171 172
        """
        Show global help.
173
        If a known command is behind the ~!help command, an adequate message is
174 175
        returned.
        """
176
        Log.debug("help command")
Mindiell's avatar
Mindiell committed
177
        # Searching for a command after help keyword
178
        command = re.search("[!~]help (help|rp|status|stats|kill|admin)", msg)
Mindiell's avatar
Mindiell committed
179 180
        if command:
            command = command.group(1)
181
            print(command)
182
            if command=="help":
183
                self.send_message(channel, helpers.help_help % user)
184
            elif command=="rp":
185
                self.send_message(channel, helpers.help_rp % user)
186
            elif command=="status":
187
                self.send_message(channel, helpers.help_status % user)
188
            elif command=="stats":
189
                self.send_message(channel, helpers.help_stats % user)
190
            elif command=="kill":
191
                self.send_message(channel, helpers.help_kill % user)
192
            elif command=="admin":
193
                self.send_message(channel, helpers.help_admin % user)
194
            else:
195
                self.send_message(channel, helpers.help_unknown % user)
Mindiell's avatar
Mindiell committed
196
        else:
197
            self.send_message(channel, helpers.help % user)
198 199

if __name__ == '__main__':
200
    Wantzel()
201
    reactor.run()