wantzel.py 20.7 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
7
8
License : AGPLv3
Doc     : https://wiki.laquadrature.net/Wantzel

TODO:
Mindiell's avatar
Mindiell committed
9
10
11
12
13
- Ajouter des commandes permettant de gérer le mediakit (moderators only)
    - Parser les urls et voir ce qu'on peut faire:
        - upload de vidéo
        - tag de vidéo
        - obtenir un lien vers une vidéo
14
15
16
"""

import re
Mindiell's avatar
Mindiell committed
17
18
import sqlite3
import time
19
20
21
22

import feedparser
from irc import IrcClientFactory
import MySQLdb
23
from twisted.internet import reactor
Mindiell's avatar
Mindiell committed
24
from twitter import Twitter, OAuth
25
26
27
28

import config
from messages import messages

Mindiell's avatar
Mindiell committed
29
30
31
32
33
34
35
36
LOG_FILE = "wantzel.log"
DEBUG = 3
WARNING = 2
INFO = 1
ERROR = 0
LOG_LEVEL = DEBUG

class Utils(object):
37
38
39
    """
    Simple utility class to log easily.
    """
Mindiell's avatar
Mindiell committed
40
41
    @classmethod
    def log(cls, message):
42
43
44
45
46
        """
        Logging message with timestamp.
        """
        actual_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        with open(LOG_FILE, 'a') as file_handle:
47
            try:
48
                file_handle.write("%s: %s\n" % (actual_time, message.encode("utf-8")))
49
50
            except UnicodeDecodeError:
                file_handle.write("%s: %s\n" % (actual_time, message))
Mindiell's avatar
Mindiell committed
51
52
53

    @classmethod
    def debug(cls, message):
54
55
56
57
        """
        Manage DEBUG level of logging.
        """
        if LOG_LEVEL >= DEBUG:
Mindiell's avatar
Mindiell committed
58
59
60
61
            cls.log("%s: %s" % ("DEBUG", message))

    @classmethod
    def warning(cls, message):
62
63
64
65
        """
        Manage WARNING level of logging.
        """
        if LOG_LEVEL >= WARNING:
Mindiell's avatar
Mindiell committed
66
67
68
69
            cls.log("%s: %s" % ("WARNING", message))

    @classmethod
    def info(cls, message):
70
71
72
73
        """
        Manage INFO level of logging.
        """
        if LOG_LEVEL >= INFO:
Mindiell's avatar
Mindiell committed
74
75
76
77
            cls.log("%s: %s" % ("INFO", message))

    @classmethod
    def error(cls, message):
78
79
80
81
        """
        Manage ERROR level of logging.
        """
        if LOG_LEVEL >= ERROR:
Mindiell's avatar
Mindiell committed
82
83
84
85
            cls.log("%s: %s" % ("ERROR", message))



Mindiell's avatar
Mindiell committed
86
87
def get_cursor():
    """
Mindiell's avatar
Mindiell committed
88
    This function connects to a MySQL database and returns a usable cursor.
Mindiell's avatar
Mindiell committed
89
    """
Mindiell's avatar
Mindiell committed
90
    connection = MySQLdb.connect(
Mindiell's avatar
Mindiell committed
91
92
93
94
95
        host=config.dbserver,
        user=config.dbuser,
        passwd=config.dbpassword,
        db=config.dbname
    )
Mindiell's avatar
Mindiell committed
96
97
    if connection:
        return connection.cursor()
Mindiell's avatar
Mindiell committed
98
99
    return None

100

101
def get_url(message):
Mindiell's avatar
Mindiell committed
102
    """
103
    Retrieve the url in the message.
Mindiell's avatar
Mindiell committed
104
    """
105
106
    # Let's get the url
    result = re.search("(https?[^ ]+)", message)
Mindiell's avatar
Mindiell committed
107
    if not result:
108
        return
109
    url = result.group(1)
Mindiell's avatar
Mindiell committed
110
111
112
113
114
115
116
117
    # Removing anchor if needed
    result = re.search("^([^#]*)", url)
    if result:
        url = result.group(1)
    # Removing trackers
    url = re.sub("[?&](utm_medium|utm_source|utm_campaign|xtor)=[^&]*", "", url)
    return url

118

Mindiell's avatar
Mindiell committed
119
120
121
122
123
124
125
def is_moderator(name):
    """
    This function verify if a user is a moderator.
    """
    connection = sqlite3.connect(config.sqlite_db)
    cursor = connection.cursor()
    cursor.execute("SELECT count(*) FROM moderator WHERE name=?", (name, ))
126
    if int(cursor.fetchone()[0]) == 1:
Mindiell's avatar
Mindiell committed
127
128
129
130
        return True
    return False


131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
def tweet(message):
    """
    Tweet message on specified account
    """
    Utils.debug("tweet method")
    auth = OAuth(
        config.TOKEN,
        config.TOKENSEC,
        config.CONSKEY,
        config.CONSSEC
    )
    twitter = Twitter(auth=auth)
    try:
        Utils.debug("Tweeting: %s" % message)
        twitter.statuses.update(status=message)
    except Exception:
        pass


150
151
152
153
154
155
156
157
class Wantzel(object):
    """
    Wantzel bot.
    """
    def __init__(self):
        """
        Initialization of bot over IRC.
        """
158
        self.number = None
Mindiell's avatar
Mindiell committed
159
160
161
162
163
164
165
166
167
        # default last_entry_published
        self.last_entry_published = time.strptime("2000-01-01", "%Y-%m-%d")
        # See if there is something in the db
        connection = sqlite3.connect(config.sqlite_db)
        for row in connection.execute("SELECT last_entry_published FROM tweets"):
            self.last_entry_published = time.strptime(
                row[0].encode("utf-8"),
                "%Y-%m-%d %H:%M:%S %Z"
            )
168
        Utils.debug("Dernier tweet: %s" % self.last_entry_published)
169
170
171
        self.irc = IrcClientFactory(config)
        self.irc.set_privmsg = self.set_privmsg
        reactor.connectTCP(config.server, config.port, self.irc)
Mindiell's avatar
Mindiell committed
172
173
174
175
176
177
178
        # Prepare timer
        reactor.callLater(config.timer, self.timer)

    def timer(self):
        """
        This method launches function regularly (see config.timer).
        """
Mindiell's avatar
Mindiell committed
179
        Utils.debug("Timer called")
Mindiell's avatar
Mindiell committed
180
181
182
183
184
        self.rp_to_twitter("http://www.laquadrature.net/fr/revue-de-presse/feed")
        self.rp_to_twitter("http://www.laquadrature.net/en/press-review/feed")
        self.count_articles()
        # Recalling the timer
        reactor.callLater(config.timer, self.timer)
185
186
187
188
189
190
191

    def set_privmsg(self):
        """
        This method set the methods to call for each callback received from IRC.
        """
        self.irc.client.privmsg = self.on_privmsg

192
    def send_message(self, channel, multiline_message):
193
194
195
        """
        Sends a message on specified channel, cutting each line in a new message
        """
196
        for message in multiline_message.splitlines():
197
198
199
200
            self.irc.client.msg(channel, message)

    def on_privmsg(self, user, channel, msg):
        """
Mindiell's avatar
Mindiell committed
201
202
        Wantzel can understand a lot of commands. Commands followed by a (*)
        are accessible only to moderators:
203
204
205
        - help
            Returns a message about how to use the bot.
            If a command is passed after help, the message explains how to use
206
            the command.
207
        - rp(acp) <url>
Mindiell's avatar
Mindiell committed
208
            Add an article in the database
209
210
        - status <url>
            Retrieve some informations about an article in the database
211
        - stats
Mindiell's avatar
Mindiell committed
212
            Show some statistics about the RP
Mindiell's avatar
Mindiell committed
213
        - kill (*)
Mindiell's avatar
Mindiell committed
214
            Kill an article by giving it a score of -100
215
        - admin list (*)
Mindiell's avatar
Mindiell committed
216
            List rights in private
217
        - admin add (*)
218
219
220
            Add one or more new moderator to list
        - admin del (*)
            Delete one or more moderator from list
221
        - admin timer
222
            Relaunch a timer
223
224
        """
        # Cleaning user name
Mindiell's avatar
Mindiell committed
225
        user = re.search("([^!]*)!", user).group(1)
Mindiell's avatar
Mindiell committed
226
        Utils.debug("Message received: %s %s %s" % (user, channel, msg))
Mindiell's avatar
Mindiell committed
227
        # Never answer to botself
228
        if user != config.nickname:
Mindiell's avatar
Mindiell committed
229
230
231
232
233
            # 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):
234
                self.help(channel, msg)
Mindiell's avatar
Mindiell committed
235
            # Find known command
236
            command = re.search("[!~](rp[acp]*|status|kill|help|stats|admin)", msg)
Mindiell's avatar
Mindiell committed
237
            Utils.debug("Command: %s" % command)
Mindiell's avatar
Mindiell committed
238
            if command:
Mindiell's avatar
Mindiell committed
239
                Utils.debug("group(0): %s" % command.group(0))
Mindiell's avatar
Mindiell committed
240
                command = command.group(1)
Mindiell's avatar
Mindiell committed
241
                Utils.debug("Command: %s" % command)
Mindiell's avatar
Mindiell committed
242
                if command.startswith("rp"):
Mindiell's avatar
Mindiell committed
243
                    Utils.debug("Calling self.rp")
244
                    self.rp(command, user, channel, msg)
245
246
247
                if command.startswith("status"):
                    Utils.debug("Calling self.status")
                    self.status(command, user, channel, msg)
248
                elif command == "help":
Mindiell's avatar
Mindiell committed
249
                    Utils.debug("Calling self.help")
250
                    self.help(user, channel, msg)
251
                elif command == "kill":
Mindiell's avatar
Mindiell committed
252
                    Utils.debug("Calling self.kill")
Mindiell's avatar
Mindiell committed
253
                    self.kill(user, channel, msg)
254
                elif command == "stats":
Mindiell's avatar
Mindiell committed
255
                    Utils.debug("Calling self.stats")
256
257
                    self.stats(channel)
                elif command == "admin":
Mindiell's avatar
Mindiell committed
258
                    Utils.debug("Calling self.admin")
Mindiell's avatar
Mindiell committed
259
                    self.admin(user, channel, msg)
Mindiell's avatar
Mindiell committed
260
261
262
        # No more giving the title of an url
        #if title and website:
        #    self.send_message(channel, messages["title"] % (title, website))
263

264
    def help(self, user, channel, msg):
265
266
        """
        Show global help.
267
        If a known command is behind the ~help command, an adequate message is
268
269
        returned.
        """
Mindiell's avatar
Mindiell committed
270
        Utils.debug("help command")
Mindiell's avatar
Mindiell committed
271
        # Searching for a command after help keyword
272
        command = re.search("~help (help|rp|status|stats|kill|admin)", msg)
Mindiell's avatar
Mindiell committed
273
274
        if command:
            command = command.group(1)
275
            self.send_message(user, messages["help_"+command])
Mindiell's avatar
Mindiell committed
276
        else:
277
            self.send_message(user, messages["help"])
278

279
    def rp(self, command, user, channel, msg):
280
281
282
        """
        Adding the article in rp database.
        """
Mindiell's avatar
Mindiell committed
283
284
285
286
        Utils.debug("rp command : %s" % command)
        Utils.debug("rp user : %s" % user)
        Utils.debug("rp channel : %s" % channel)
        Utils.debug("rp msg : %s" % msg)
287
        cite = 0
288
        note = 1
289
        url = get_url(msg)
Mindiell's avatar
Mindiell committed
290
        Utils.debug("url: %s" % url)
291
        if not url:
Mindiell's avatar
Mindiell committed
292
            return
293

Mindiell's avatar
Mindiell committed
294
295
        # Looking for such an article in database
        cursor = get_cursor()
296
297
298
299
300
301
302
        cursor.execute("SELECT id, note, provenance FROM presse WHERE url = %s", (url, ))
        rows = cursor.fetchall()
        if not rows:
            # LQdN is quoted
            if "c" in command:
                cite += 2
            # the article speak about LQdN
303
            if command.count("p") > 1:
304
305
306
                cite += 2
            # Archive this article
            if "a" in command:
307
                note = -2
Mindiell's avatar
Mindiell committed
308
            Utils.debug("Adding an article by %s: %s" % (user, url))
309
310
            cursor.execute(
                """INSERT INTO presse SET
311
                url=%s, provenance=%s, cite=%s, note=%s, datec=NOW(), title='',
312
313
                lang='', published=0, nid=0, screenshot=0, fetched=0, seemscite=0
                """,
314
                (url, user, cite, note)
315
            )
Mindiell's avatar
Mindiell committed
316
            self.send_message(channel, messages["rp_new_article"] % user)
317
        else:
318
            if rows[0][2] != user:
Mindiell's avatar
Mindiell committed
319
                Utils.debug("Adding a point by %s on %s" % (user, rows[0][0]))
320
                cursor.execute(
321
322
323
                    "UPDATE presse SET note=note+1 WHERE id=%s",
                    (rows[0][0], )
                )
324
            if (rows[0][1]+1) < 3:
Mindiell's avatar
Mindiell committed
325
                self.send_message(channel, messages["rp_known_article"] % user)
326
            else:
Mindiell's avatar
Mindiell committed
327
                self.send_message(channel, messages["rp_taken_article"] % user)
Mindiell's avatar
Mindiell committed
328
329
                # Update number of articles to do
                self.count_articles()
330

331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
    def status(self, command, user, channel, msg):
        """
        Retrieving status of the article in rp database.
        """
        Utils.debug("rp command : %s" % command)
        Utils.debug("rp user : %s" % user)
        Utils.debug("rp channel : %s" % channel)
        Utils.debug("rp msg : %s" % msg)
        url = get_url(msg)
        Utils.debug("url: %s" % url)
        if not url:
            return

        # Looking for such an article in database
        cursor = get_cursor()
        cursor.execute("""
            SELECT cite, published, nid, screenshot, fetched, seemscite, note
            FROM presse
            WHERE url = %s""", (url, ))
        rows = cursor.fetchall()
        if not rows:
            self.send_message(channel, messages["status_unknown_article"] % user)
        else:
            message = "%s: note %s / " % (user, rows[0][6])
            if rows[0][0] != "0":
                message += "cite lqdn / "
            if rows[0][1] == 1:
                published = "publié (noeud %s) / " % rows[0][2]
            else:
                published = "non publié / "
            self.send_message(channel, message[:-2])

363
364
    def kill(self, user, channel, msg):
        """
Mindiell's avatar
Mindiell committed
365
        Kill an article by setting its score to -100.
366
        """
Mindiell's avatar
Mindiell committed
367
        Utils.debug("kill command")
Mindiell's avatar
Mindiell committed
368
369
        if is_moderator(user):
            url = get_url(msg)
Mindiell's avatar
Mindiell committed
370
            Utils.debug("url: %s" % url)
371
            if url == "":
Mindiell's avatar
Mindiell committed
372
                return
373
            elif url == "http":
Mindiell's avatar
Mindiell committed
374
375
376
377
378
379
380
381
382
383
384
                self.send_message(channel, messages["rp_http"] % user)
                return
            # Looking for such an article in database
            cursor = get_cursor()
            cursor.execute("SELECT id, note FROM presse WHERE url=%s", (url, ))
            rows = cursor.fetchall()
            if not rows:
                self.send_message(channel, messages["kill_none"] % url)
            else:
                cursor.execute("UPDATE presse SET note=-100 WHERE id=%s", (rows[0][0], ))
                self.send_message(channel, messages["kill_done"] % url)
Mindiell's avatar
Mindiell committed
385
        else:
Mindiell's avatar
Mindiell committed
386
            self.send_message(channel, messages["not_moderator"])
387

388
    def stats(self, channel):
389
390
391
        """
        Returns stats on articles in press review.
        """
Mindiell's avatar
Mindiell committed
392
        Utils.debug("stats command")
Mindiell's avatar
Mindiell committed
393
        cursor = get_cursor()
394
        periods = [1, 3, 7, 15]
395
        notes = [0, 3, 4]
396
397
398
399
400
        notnull = 0
        somethingatall = 0
        result = ""
        for note in notes:
            notnull = 0
401
            period_result = ""
402
403
            for period in periods:
                cursor.execute(
404
                    """SELECT COUNT(id) AS cid FROM presse
405
406
407
408
409
410
                    WHERE nid=0
                    AND datec>(NOW()-INTERVAL %s DAY)
                    AND note>=%s""",
                    (period, note)
                )
                rows = cursor.fetchall()
411
                if rows[0][0] > 0:
412
                    period_result = period_result + "%sj:%s, " % (period, rows[0][0])
413
                    notnull = 1
Mindiell's avatar
Mindiell committed
414
                    somethingatall = 1
415
            if notnull:
416
                result = result + "note>=%s: " % note + period_result[:-2] + "\n"
417
        if somethingatall == 0:
Mindiell's avatar
Mindiell committed
418
            result = messages["stats_bravo"] % periods[-1]
419
420
        self.send_message(channel, result)

Mindiell's avatar
Mindiell committed
421
422
423
    def admin(self, user, channel, msg):
        """
        Manage moderation.
424
        A sub-command should be behind the ~admin command.
Mindiell's avatar
Mindiell committed
425
        """
Mindiell's avatar
Mindiell committed
426
        Utils.debug("admin command")
Mindiell's avatar
Mindiell committed
427
        # Searching for a command after admin keyword
Mindiell's avatar
Mindiell committed
428
        command = re.search("~admin (list|add|del|timer)", msg)
Mindiell's avatar
Mindiell committed
429
430
        if command:
            command = command.group(1)
431
432
433
            if command == "list":
                self.admin_list(user, channel)
            elif command == "add":
Mindiell's avatar
Mindiell committed
434
                self.admin_add(user, channel, msg)
435
            elif command == "del":
Mindiell's avatar
Mindiell committed
436
                self.admin_del(user, channel, msg)
437
438
            elif command == "timer":
                self.admin_timer(user, channel)
Mindiell's avatar
Mindiell committed
439

440
    def admin_list(self, user, channel):
Mindiell's avatar
Mindiell committed
441
442
443
        """
        List actual moderators.
        """
Mindiell's avatar
Mindiell committed
444
        Utils.debug("admin_list command")
Mindiell's avatar
Mindiell committed
445
446
447
448
449
450
451
452
453
454
455
456
457
        if is_moderator(user):
            connection = sqlite3.connect(config.sqlite_db)
            names = []
            for row in connection.execute("SELECT name FROM moderator"):
                names.append(row[0].encode("utf-8"))
            self.send_message(channel, messages["admin_list"] % ", ".join(names))
        else:
            self.send_message(channel, messages["not_moderator"])

    def admin_add(self, user, channel, msg):
        """
        Add some new moderators if not existing yet.
        """
Mindiell's avatar
Mindiell committed
458
        Utils.debug("admin_add command")
Mindiell's avatar
Mindiell committed
459
460
461
462
        if is_moderator(user):
            try:
                names = []
                connection = sqlite3.connect(config.sqlite_db)
463
                result = re.search("~admin add (([^,]+, ?)+)?(.*)", msg)
Mindiell's avatar
Mindiell committed
464
                if result.group(1):
465
                    names = [name for name in result.group(1).split(", ") if name != ""]
Mindiell's avatar
Mindiell committed
466
467
468
469
470
                names.append(result.group(3))
                # Do not add actual moderators
                moderators = []
                for row in connection.execute("SELECT name FROM moderator"):
                    moderators.append(row[0].encode("utf-8"))
471
                names = list(set([name for name in names if name not in moderators]))
Mindiell's avatar
Mindiell committed
472
473
474
475
476
477
478
479
                if names:
                    # Converting set in list of tuples
                    values = [(name,) for name in names]
                    connection.executemany("INSERT INTO moderator (name) VALUES (?)", values)
                    connection.commit()
                    self.send_message(channel, messages["admin_add"] % ", ".join(names))
                else:
                    self.send_message(channel, messages["admin_add_empty"])
480
            except Exception:
Mindiell's avatar
Mindiell committed
481
482
483
484
485
486
487
488
                pass
        else:
            self.send_message(channel, messages["not_moderator"])

    def admin_del(self, user, channel, msg):
        """
        Delete a moderator from list.
        """
Mindiell's avatar
Mindiell committed
489
        Utils.debug("admin_del command")
Mindiell's avatar
Mindiell committed
490
491
492
        if is_moderator(user):
            try:
                names = []
493
                result = re.search("~admin del (([^,]+, ?)+)?(.*)", msg)
Mindiell's avatar
Mindiell committed
494
                if result.group(1):
495
                    names = [name for name in result.group(1).split(", ") if name != ""]
Mindiell's avatar
Mindiell committed
496
                names.append(result.group(3))
497
                names = list(set(names))
Mindiell's avatar
Mindiell committed
498
                Utils.debug(names)
Mindiell's avatar
Mindiell committed
499
500
501
502
503
                connection = sqlite3.connect(config.sqlite_db)
                for name in names:
                    connection.execute("DELETE FROM moderator WHERE name=?", (name, ))
                connection.commit()
                self.send_message(channel, messages["admin_del"] % ", ".join(names))
504
            except Exception:
Mindiell's avatar
Mindiell committed
505
506
507
508
                pass
        else:
            self.send_message(channel, messages["not_moderator"])

509
    def admin_timer(self, user, channel):
510
511
512
513
514
515
516
517
        """
        Relaunch a timer.
        """
        Utils.debug("admin_timer command")
        if is_moderator(user):
            try:
                # Recalling the timer
                reactor.callLater(config.timer, self.timer)
518
            except Exception:
519
520
521
522
                pass
        else:
            self.send_message(channel, messages["not_moderator"])

Mindiell's avatar
Mindiell committed
523
524
525
526
527
    def count_articles(self):
        """
        Count number of articles not done in RP and updates the topic of the
        press review channel if needed.
        """
Mindiell's avatar
Mindiell committed
528
        Utils.debug("count_articles method")
Mindiell's avatar
Mindiell committed
529
530
531
532
533
534
535
        cursor = get_cursor()
        cursor.execute("""SELECT COUNT(*) FROM presse
            WHERE DATE_SUB(NOW(), INTERVAL 2 MONTH)<datec
            AND note > 2
            AND nid = 0""")
        rows = cursor.fetchall()
        number = int(rows[0][0])
536
        Utils.debug("Found %s articles." % number)
537
        if self.number != number:
Mindiell's avatar
Mindiell committed
538
            self.irc.client.topic("#lqdn-rp", messages["topic"] % number)
Mindiell's avatar
Mindiell committed
539
540
541
542
543
544
        self.number = number

    def rp_to_twitter(self, rss):
        """
        By parsing the RSS feed of the press-review, we know what to tweet.
        """
Mindiell's avatar
Mindiell committed
545
        Utils.debug("rp_to_twitter method")
Mindiell's avatar
Mindiell committed
546
547
        now = time.localtime()
        today = time.strptime("%s-%s-%s %s" % (
548
549
550
551
552
            now.tm_year,
            now.tm_mon,
            now.tm_mday,
            time.tzname[0]
            ), "%Y-%m-%d %Z")
Mindiell's avatar
Mindiell committed
553
554
555
        language = "fr"
        if "/en/" in rss:
            language = "en"
Mindiell's avatar
Mindiell committed
556
557
        entries = feedparser.parse(rss)['entries']
        entries.reverse()
558
        Utils.debug(self.last_entry_published)
Mindiell's avatar
Mindiell committed
559
560
561
562
563
        for entry in entries:
            # if date of publication is greater than today, midnight, and
            # lesser than future
            if today < entry.published_parsed < now:
                if self.last_entry_published < entry.published_parsed:
okhin's avatar
okhin committed
564
565
566
567
                    # Let's see if we can truncate the lenght of the tweet
                    # We have 5 chars for the language, so max-length is 135
                    title = entry.title.encode("utf-8")
                    link = entry.link.encode("utf-8")
568
                    if len(title) + min(len(link),23) > 135:
okhin's avatar
okhin committed
569
                        # What is the number of chars we need to remove
570
571
                        excess = len(title) + min(len(link),23) - 135
                        title = ''.join([title[:-(excess + 4)], ' ...'])
572
                    tweet(messages["tweet_rp_%s" % language] % (
okhin's avatar
okhin committed
573
574
                        title,
                        link
Mindiell's avatar
Mindiell committed
575
                    ))
Mindiell's avatar
Mindiell committed
576
                    Utils.debug(entry.published_parsed)
Mindiell's avatar
Mindiell committed
577
                    Utils.debug(entry.title)
Mindiell's avatar
Mindiell committed
578
579
580
581
582
583
584
585
586
587
588
589
590
591
                    # Save last_entry_published
                    self.last_entry_published = entry.published_parsed
                    last_entry_published = time.strftime(
                        "%Y-%m-%d %H:%M:%S %Z",
                        self.last_entry_published
                    )
                    connection = sqlite3.connect(config.sqlite_db)
                    connection.execute(
                        "UPDATE tweets SET last_entry_published=?",
                        (last_entry_published,)
                    )
                    connection.commit()
                    # Tweet only one message in order not to spam
                    return
592
593
594
                else:
                    Utils.debug(entry.title)
                    Utils.debug(entry.published_parsed)
Mindiell's avatar
Mindiell committed
595

596
597

if __name__ == '__main__':
598
    Wantzel()
599
    reactor.run()