notify.py 21.7 KB
Newer Older
1
#!/usr/bin/env python3
2
3
4
5
6
7

import email.mime.text
import subprocess
import sys
import textwrap

Lukas Fleischer's avatar
Lukas Fleischer committed
8
9
import aurweb.config
import aurweb.db
Lukas Fleischer's avatar
Lukas Fleischer committed
10
import aurweb.l10n
11

Lukas Fleischer's avatar
Lukas Fleischer committed
12
aur_location = aurweb.config.get('options', 'aur_location')
13
14


15
16
17
def headers_cc(cclist):
    return {'Cc': str.join(', ', cclist)}

18

19
20
21
def headers_msgid(thread_id):
    return {'Message-ID': thread_id}

22

23
24
25
def headers_reply(thread_id):
    return {'In-Reply-To': thread_id, 'References': thread_id}

26

Lukas Fleischer's avatar
Lukas Fleischer committed
27
28
def username_from_id(conn, uid):
    cur = conn.execute('SELECT UserName FROM Users WHERE ID = ?', [uid])
29
30
    return cur.fetchone()[0]

31

Lukas Fleischer's avatar
Lukas Fleischer committed
32
33
34
def pkgbase_from_id(conn, pkgbase_id):
    cur = conn.execute('SELECT Name FROM PackageBases WHERE ID = ?',
                       [pkgbase_id])
35
36
    return cur.fetchone()[0]

37

Lukas Fleischer's avatar
Lukas Fleischer committed
38
39
40
def pkgbase_from_pkgreq(conn, reqid):
    cur = conn.execute('SELECT PackageBaseID FROM PackageRequests ' +
                       'WHERE ID = ?', [reqid])
41
42
    return cur.fetchone()[0]

43

44
class Notification:
Lukas Fleischer's avatar
Lukas Fleischer committed
45
46
47
    def __init__(self):
        self._l10n = aurweb.l10n.Translator()

48
49
50
51
52
53
    def get_refs(self):
        return ()

    def get_headers(self):
        return {}

Lukas Fleischer's avatar
Lukas Fleischer committed
54
    def get_body_fmt(self, lang):
55
        body = ''
Lukas Fleischer's avatar
Lukas Fleischer committed
56
        for line in self.get_body(lang).splitlines():
57
58
59
            body += textwrap.fill(line, break_long_words=False) + '\n'
        for i, ref in enumerate(self.get_refs()):
            body += '\n' + '[%d] %s' % (i + 1, ref)
Lukas Fleischer's avatar
Lukas Fleischer committed
60
        return body.rstrip()
61

Lukas Fleischer's avatar
Lukas Fleischer committed
62
    def send(self):
63
64
65
        sendmail = aurweb.config.get('notifications', 'sendmail')
        sender = aurweb.config.get('notifications', 'sender')
        reply_to = aurweb.config.get('notifications', 'reply-to')
66
67
68
        reason = self.__class__.__name__
        if reason.endswith('Notification'):
            reason = reason[:-len('Notification')]
69
70

        for recipient in self.get_recipients():
Lukas Fleischer's avatar
Lukas Fleischer committed
71
72
73
74
            to, lang = recipient
            msg = email.mime.text.MIMEText(self.get_body_fmt(lang),
                                           'plain', 'utf-8')
            msg['Subject'] = self.get_subject(lang)
75
76
            msg['From'] = sender
            msg['Reply-to'] = reply_to
Lukas Fleischer's avatar
Lukas Fleischer committed
77
            msg['To'] = to
78
            msg['X-AUR-Reason'] = reason
79
80
81
82
83
84
85
86
87
88
89

            for key, value in self.get_headers().items():
                msg[key] = value

            p = subprocess.Popen([sendmail, '-t', '-oi'],
                                 stdin=subprocess.PIPE)
            p.communicate(msg.as_bytes())


class ResetKeyNotification(Notification):
    def __init__(self, conn, uid):
Lukas Fleischer's avatar
Lukas Fleischer committed
90
91
92
93
        cur = conn.execute('SELECT UserName, Email, LangPreference, ' +
                           'ResetKey FROM Users WHERE ID = ?', [uid])
        self._username, self._to, self._lang, self._resetkey = cur.fetchone()
        super().__init__()
94
95

    def get_recipients(self):
Lukas Fleischer's avatar
Lukas Fleischer committed
96
        return [(self._to, self._lang)]
97

Lukas Fleischer's avatar
Lukas Fleischer committed
98
99
    def get_subject(self, lang):
        return self._l10n.translate('AUR Password Reset', lang)
100

Lukas Fleischer's avatar
Lukas Fleischer committed
101
102
    def get_body(self, lang):
        return self._l10n.translate(
103
104
105
106
107
                'A password reset request was submitted for the account '
                '{user} associated with your email address. If you wish to '
                'reset your password follow the link [1] below, otherwise '
                'ignore this message and nothing will happen.',
                lang).format(user=self._username)
108
109
110
111
112
113

    def get_refs(self):
        return (aur_location + '/passreset/?resetkey=' + self._resetkey,)


class WelcomeNotification(ResetKeyNotification):
Lukas Fleischer's avatar
Lukas Fleischer committed
114
115
116
    def get_subject(self, lang):
        return self._l10n.translate('Welcome to the Arch User Repository',
                                    lang)
117

Lukas Fleischer's avatar
Lukas Fleischer committed
118
119
120
121
122
123
    def get_body(self, lang):
        return self._l10n.translate(
                'Welcome to the Arch User Repository! In order to set an '
                'initial password for your new account, please click the '
                'link [1] below. If the link does not work, try copying and '
                'pasting it into your browser.', lang)
124
125
126
127
128
129


class CommentNotification(Notification):
    def __init__(self, conn, uid, pkgbase_id, comment_id):
        self._user = username_from_id(conn, uid)
        self._pkgbase = pkgbase_from_id(conn, pkgbase_id)
Lukas Fleischer's avatar
Lukas Fleischer committed
130
131
        cur = conn.execute('SELECT DISTINCT Users.Email, Users.LangPreference '
                           'FROM Users INNER JOIN PackageNotifications ' +
132
133
134
135
136
                           'ON PackageNotifications.UserID = Users.ID WHERE ' +
                           'Users.CommentNotify = 1 AND ' +
                           'PackageNotifications.UserID != ? AND ' +
                           'PackageNotifications.PackageBaseID = ?',
                           [uid, pkgbase_id])
Lukas Fleischer's avatar
Lukas Fleischer committed
137
        self._recipients = cur.fetchall()
138
139
140
        cur = conn.execute('SELECT Comments FROM PackageComments WHERE ID = ?',
                           [comment_id])
        self._text = cur.fetchone()[0]
Lukas Fleischer's avatar
Lukas Fleischer committed
141
        super().__init__()
142
143

    def get_recipients(self):
Lukas Fleischer's avatar
Lukas Fleischer committed
144
        return self._recipients
145

Lukas Fleischer's avatar
Lukas Fleischer committed
146
    def get_subject(self, lang):
147
148
        return self._l10n.translate('AUR Comment for {pkgbase}',
                                    lang).format(pkgbase=self._pkgbase)
149

Lukas Fleischer's avatar
Lukas Fleischer committed
150
151
    def get_body(self, lang):
        body = self._l10n.translate(
152
153
                '{user} [1] added the following comment to {pkgbase} [2]:',
                lang).format(user=self._user, pkgbase=self._pkgbase)
154
        body += '\n\n' + self._text + '\n\n-- \n'
Lukas Fleischer's avatar
Lukas Fleischer committed
155
156
157
158
        dnlabel = self._l10n.translate('Disable notifications', lang)
        body += self._l10n.translate(
                'If you no longer wish to receive notifications about this '
                'package, please go to the package page [2] and select '
159
                '"{label}".', lang).format(label=dnlabel)
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
        return body

    def get_refs(self):
        return (aur_location + '/account/' + self._user + '/',
                aur_location + '/pkgbase/' + self._pkgbase + '/')

    def get_headers(self):
        thread_id = '<pkg-notifications-' + self._pkgbase + \
                    '@aur.archlinux.org>'
        return headers_reply(thread_id)


class UpdateNotification(Notification):
    def __init__(self, conn, uid, pkgbase_id):
        self._user = username_from_id(conn, uid)
        self._pkgbase = pkgbase_from_id(conn, pkgbase_id)
Lukas Fleischer's avatar
Lukas Fleischer committed
176
177
        cur = conn.execute('SELECT DISTINCT Users.Email, ' +
                           'Users.LangPreference FROM Users ' +
178
179
180
181
182
183
                           'INNER JOIN PackageNotifications ' +
                           'ON PackageNotifications.UserID = Users.ID WHERE ' +
                           'Users.UpdateNotify = 1 AND ' +
                           'PackageNotifications.UserID != ? AND ' +
                           'PackageNotifications.PackageBaseID = ?',
                           [uid, pkgbase_id])
Lukas Fleischer's avatar
Lukas Fleischer committed
184
185
        self._recipients = cur.fetchall()
        super().__init__()
186
187

    def get_recipients(self):
Lukas Fleischer's avatar
Lukas Fleischer committed
188
        return self._recipients
189

Lukas Fleischer's avatar
Lukas Fleischer committed
190
    def get_subject(self, lang):
191
192
        return self._l10n.translate('AUR Package Update: {pkgbase}',
                                    lang).format(pkgbase=self._pkgbase)
193

Lukas Fleischer's avatar
Lukas Fleischer committed
194
    def get_body(self, lang):
195
196
197
198
        body = self._l10n.translate('{user} [1] pushed a new commit to '
                                    '{pkgbase} [2].', lang).format(
                                            user=self._user,
                                            pkgbase=self._pkgbase)
199
        body += '\n\n-- \n'
Lukas Fleischer's avatar
Lukas Fleischer committed
200
201
202
203
        dnlabel = self._l10n.translate('Disable notifications', lang)
        body += self._l10n.translate(
                'If you no longer wish to receive notifications about this '
                'package, please go to the package page [2] and select '
204
                '"{label}".', lang).format(label=dnlabel)
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
        return body

    def get_refs(self):
        return (aur_location + '/account/' + self._user + '/',
                aur_location + '/pkgbase/' + self._pkgbase + '/')

    def get_headers(self):
        thread_id = '<pkg-notifications-' + self._pkgbase + \
                    '@aur.archlinux.org>'
        return headers_reply(thread_id)


class FlagNotification(Notification):
    def __init__(self, conn, uid, pkgbase_id):
        self._user = username_from_id(conn, uid)
        self._pkgbase = pkgbase_from_id(conn, pkgbase_id)
Lukas Fleischer's avatar
Lukas Fleischer committed
221
222
        cur = conn.execute('SELECT DISTINCT Users.Email, ' +
                           'Users.LangPreference FROM Users ' +
223
224
225
226
227
228
                           'LEFT JOIN PackageComaintainers ' +
                           'ON PackageComaintainers.UsersID = Users.ID ' +
                           'INNER JOIN PackageBases ' +
                           'ON PackageBases.MaintainerUID = Users.ID OR ' +
                           'PackageBases.ID = PackageComaintainers.PackageBaseID ' +
                           'WHERE PackageBases.ID = ?', [pkgbase_id])
Lukas Fleischer's avatar
Lukas Fleischer committed
229
        self._recipients = cur.fetchall()
230
231
232
        cur = conn.execute('SELECT FlaggerComment FROM PackageBases WHERE ' +
                           'ID = ?', [pkgbase_id])
        self._text = cur.fetchone()[0]
Lukas Fleischer's avatar
Lukas Fleischer committed
233
        super().__init__()
234
235

    def get_recipients(self):
Lukas Fleischer's avatar
Lukas Fleischer committed
236
        return self._recipients
237

Lukas Fleischer's avatar
Lukas Fleischer committed
238
    def get_subject(self, lang):
239
240
241
        return self._l10n.translate('AUR Out-of-date Notification for '
                                    '{pkgbase}',
                                    lang).format(pkgbase=self._pkgbase)
242

Lukas Fleischer's avatar
Lukas Fleischer committed
243
244
    def get_body(self, lang):
        body = self._l10n.translate(
245
246
247
                'Your package {pkgbase} [1] has been flagged out-of-date by '
                '{user} [2]:', lang).format(pkgbase=self._pkgbase,
                                            user=self._user)
248
249
250
251
252
253
254
255
256
257
258
259
        body += '\n\n' + self._text
        return body

    def get_refs(self):
        return (aur_location + '/pkgbase/' + self._pkgbase + '/',
                aur_location + '/account/' + self._user + '/')


class OwnershipEventNotification(Notification):
    def __init__(self, conn, uid, pkgbase_id):
        self._user = username_from_id(conn, uid)
        self._pkgbase = pkgbase_from_id(conn, pkgbase_id)
Lukas Fleischer's avatar
Lukas Fleischer committed
260
261
        cur = conn.execute('SELECT DISTINCT Users.Email, ' +
                           'Users.LangPreference FROM Users ' +
262
263
264
265
266
267
                           'INNER JOIN PackageNotifications ' +
                           'ON PackageNotifications.UserID = Users.ID WHERE ' +
                           'Users.OwnershipNotify = 1 AND ' +
                           'PackageNotifications.UserID != ? AND ' +
                           'PackageNotifications.PackageBaseID = ?',
                           [uid, pkgbase_id])
Lukas Fleischer's avatar
Lukas Fleischer committed
268
        self._recipients = cur.fetchall()
269
270
271
        cur = conn.execute('SELECT FlaggerComment FROM PackageBases WHERE ' +
                           'ID = ?', [pkgbase_id])
        self._text = cur.fetchone()[0]
Lukas Fleischer's avatar
Lukas Fleischer committed
272
        super().__init__()
273
274

    def get_recipients(self):
Lukas Fleischer's avatar
Lukas Fleischer committed
275
        return self._recipients
276

Lukas Fleischer's avatar
Lukas Fleischer committed
277
    def get_subject(self, lang):
278
279
        return self._l10n.translate('AUR Ownership Notification for {pkgbase}',
                                    lang).format(pkgbase=self._pkgbase)
280
281
282
283
284
285
286

    def get_refs(self):
        return (aur_location + '/pkgbase/' + self._pkgbase + '/',
                aur_location + '/account/' + self._user + '/')


class AdoptNotification(OwnershipEventNotification):
Lukas Fleischer's avatar
Lukas Fleischer committed
287
288
    def get_body(self, lang):
        return self._l10n.translate(
289
290
                'The package {pkgbase} [1] was adopted by {user} [2].',
                lang).format(pkgbase=self._pkgbase, user=self._user)
291
292
293


class DisownNotification(OwnershipEventNotification):
294
    def get_body(self, lang):
Lukas Fleischer's avatar
Lukas Fleischer committed
295
        return self._l10n.translate(
296
297
298
                'The package {pkgbase} [1] was disowned by {user} '
                '[2].', lang).format(pkgbase=self._pkgbase,
                                     user=self._user)
299
300
301
302
303


class ComaintainershipEventNotification(Notification):
    def __init__(self, conn, uid, pkgbase_id):
        self._pkgbase = pkgbase_from_id(conn, pkgbase_id)
Lukas Fleischer's avatar
Lukas Fleischer committed
304
305
306
307
        cur = conn.execute('SELECT Email, LangPreference FROM Users ' +
                           'WHERE ID = ?', [uid])
        self._to, self._lang = cur.fetchone()
        super().__init__()
308
309

    def get_recipients(self):
Lukas Fleischer's avatar
Lukas Fleischer committed
310
        return [(self._to, self._lang)]
311

Lukas Fleischer's avatar
Lukas Fleischer committed
312
    def get_subject(self, lang):
313
314
315
        return self._l10n.translate('AUR Co-Maintainer Notification for '
                                    '{pkgbase}',
                                    lang).format(pkgbase=self._pkgbase)
316
317
318
319
320
321

    def get_refs(self):
        return (aur_location + '/pkgbase/' + self._pkgbase + '/',)


class ComaintainerAddNotification(ComaintainershipEventNotification):
Lukas Fleischer's avatar
Lukas Fleischer committed
322
323
    def get_body(self, lang):
        return self._l10n.translate(
324
325
                'You were added to the co-maintainer list of {pkgbase} [1].',
                lang).format(pkgbase=self._pkgbase)
326
327
328


class ComaintainerRemoveNotification(ComaintainershipEventNotification):
Lukas Fleischer's avatar
Lukas Fleischer committed
329
330
    def get_body(self, lang):
        return self._l10n.translate(
331
332
                'You were removed from the co-maintainer list of {pkgbase} '
                '[1].', lang).format(pkgbase=self._pkgbase)
333
334
335
336
337
338
339
340
341
342


class DeleteNotification(Notification):
    def __init__(self, conn, uid, old_pkgbase_id, new_pkgbase_id=None):
        self._user = username_from_id(conn, uid)
        self._old_pkgbase = pkgbase_from_id(conn, old_pkgbase_id)
        if new_pkgbase_id:
            self._new_pkgbase = pkgbase_from_id(conn, new_pkgbase_id)
        else:
            self._new_pkgbase = None
Lukas Fleischer's avatar
Lukas Fleischer committed
343
344
        cur = conn.execute('SELECT DISTINCT Users.Email, ' +
                           'Users.LangPreference FROM Users ' +
345
346
347
348
349
                           'INNER JOIN PackageNotifications ' +
                           'ON PackageNotifications.UserID = Users.ID WHERE ' +
                           'PackageNotifications.UserID != ? AND ' +
                           'PackageNotifications.PackageBaseID = ?',
                           [uid, old_pkgbase_id])
Lukas Fleischer's avatar
Lukas Fleischer committed
350
351
        self._recipients = cur.fetchall()
        super().__init__()
352
353

    def get_recipients(self):
Lukas Fleischer's avatar
Lukas Fleischer committed
354
        return self._recipients
355

Lukas Fleischer's avatar
Lukas Fleischer committed
356
    def get_subject(self, lang):
357
358
        return self._l10n.translate('AUR Package deleted: {pkgbase}',
                                    lang).format(pkgbase=self._old_pkgbase)
359

Lukas Fleischer's avatar
Lukas Fleischer committed
360
    def get_body(self, lang):
361
        if self._new_pkgbase:
Lukas Fleischer's avatar
Lukas Fleischer committed
362
363
            dnlabel = self._l10n.translate('Disable notifications', lang)
            return self._l10n.translate(
364
                    '{user} [1] merged {old} [2] into {new} [3].\n\n'
365
                    '-- \n'
Lukas Fleischer's avatar
Lukas Fleischer committed
366
                    'If you no longer wish receive notifications about the '
367
368
369
                    'new package, please go to [3] and click "{label}".',
                    lang).format(user=self._user, old=self._old_pkgbase,
                                 new=self._new_pkgbase, label=dnlabel)
370
        else:
Lukas Fleischer's avatar
Lukas Fleischer committed
371
            return self._l10n.translate(
372
                    '{user} [1] deleted {pkgbase} [2].\n\n'
Lukas Fleischer's avatar
Lukas Fleischer committed
373
                    'You will no longer receive notifications about this '
374
375
                    'package.', lang).format(user=self._user,
                                             pkgbase=self._old_pkgbase)
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405

    def get_refs(self):
        refs = (aur_location + '/account/' + self._user + '/',
                aur_location + '/pkgbase/' + self._old_pkgbase + '/')
        if self._new_pkgbase:
            refs += (aur_location + '/pkgbase/' + self._new_pkgbase + '/',)
        return refs


class RequestOpenNotification(Notification):
    def __init__(self, conn, uid, reqid, reqtype, pkgbase_id, merge_into=None):
        self._user = username_from_id(conn, uid)
        self._pkgbase = pkgbase_from_id(conn, pkgbase_id)
        cur = conn.execute('SELECT DISTINCT Users.Email FROM PackageRequests ' +
                           'INNER JOIN PackageBases ' +
                           'ON PackageBases.ID = PackageRequests.PackageBaseID ' +
                           'INNER JOIN Users ' +
                           'ON Users.ID = PackageRequests.UsersID ' +
                           'OR Users.ID = PackageBases.MaintainerUID ' +
                           'WHERE PackageRequests.ID = ?', [reqid])
        self._to = aurweb.config.get('options', 'aur_request_ml')
        self._cc = [row[0] for row in cur.fetchall()]
        cur = conn.execute('SELECT Comments FROM PackageRequests WHERE ID = ?',
                           [reqid])
        self._text = cur.fetchone()[0]
        self._reqid = int(reqid)
        self._reqtype = reqtype
        self._merge_into = merge_into

    def get_recipients(self):
Lukas Fleischer's avatar
Lukas Fleischer committed
406
        return [(self._to, 'en')]
407

Lukas Fleischer's avatar
Lukas Fleischer committed
408
    def get_subject(self, lang):
409
410
411
        return '[PRQ#%d] %s Request for %s' % \
               (self._reqid, self._reqtype.title(), self._pkgbase)

Lukas Fleischer's avatar
Lukas Fleischer committed
412
    def get_body(self, lang):
413
414
415
416
417
        if self._merge_into:
            body = '%s [1] filed a request to merge %s [2] into %s [3]:' % \
                   (self._user, self._pkgbase, self._merge_into)
            body += '\n\n' + self._text
        else:
418
419
420
            an = 'an' if self._reqtype[0] in 'aeiou' else 'a'
            body = '%s [1] filed %s %s request for %s [2]:' % \
                   (self._user, an, self._reqtype, self._pkgbase)
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
            body += '\n\n' + self._text
        return body

    def get_refs(self):
        refs = (aur_location + '/account/' + self._user + '/',
                aur_location + '/pkgbase/' + self._pkgbase + '/')
        if self._merge_into:
            refs += (aur_location + '/pkgbase/' + self._merge_into + '/',)
        return refs

    def get_headers(self):
        thread_id = '<pkg-request-' + str(self._reqid) + '@aur.archlinux.org>'
        # Use a deterministic Message-ID for the first email referencing a
        # request.
        headers = headers_msgid(thread_id)
        headers.update(headers_cc(self._cc))
        return headers


class RequestCloseNotification(Notification):
    def __init__(self, conn, uid, reqid, reason):
        self._user = username_from_id(conn, uid) if int(uid) else None
        cur = conn.execute('SELECT DISTINCT Users.Email FROM PackageRequests ' +
                           'INNER JOIN PackageBases ' +
                           'ON PackageBases.ID = PackageRequests.PackageBaseID ' +
                           'INNER JOIN Users ' +
                           'ON Users.ID = PackageRequests.UsersID ' +
                           'OR Users.ID = PackageBases.MaintainerUID ' +
                           'WHERE PackageRequests.ID = ?', [reqid])
        self._to = aurweb.config.get('options', 'aur_request_ml')
        self._cc = [row[0] for row in cur.fetchall()]
452
453
454
455
456
457
458
459
        cur = conn.execute('SELECT PackageRequests.ClosureComment, ' +
                           'RequestTypes.Name, ' +
                           'PackageRequests.PackageBaseName ' +
                           'FROM PackageRequests ' +
                           'INNER JOIN RequestTypes ' +
                           'ON RequestTypes.ID = PackageRequests.ReqTypeID ' +
                           'WHERE PackageRequests.ID = ?', [reqid])
        self._text, self._reqtype, self._pkgbase = cur.fetchone()
460
461
462
463
        self._reqid = int(reqid)
        self._reason = reason

    def get_recipients(self):
Lukas Fleischer's avatar
Lukas Fleischer committed
464
        return [(self._to, 'en')]
465

Lukas Fleischer's avatar
Lukas Fleischer committed
466
    def get_subject(self, lang):
467
468
469
470
        return '[PRQ#%d] %s Request for %s %s' % (self._reqid,
                                                  self._reqtype.title(),
                                                  self._pkgbase,
                                                  self._reason.title())
471

Lukas Fleischer's avatar
Lukas Fleischer committed
472
    def get_body(self, lang):
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
        if self._user:
            body = 'Request #%d has been %s by %s [1]' % \
                   (self._reqid, self._reason, self._user)
        else:
            body = 'Request #%d has been %s automatically by the Arch User ' \
                   'Repository package request system' % \
                   (self._reqid, self._reason)
        if self._text.strip() == '':
            body += '.'
        else:
            body += ':\n\n' + self._text
        return body

    def get_refs(self):
        if self._user:
            return (aur_location + '/account/' + self._user + '/',)
        else:
            return ()

    def get_headers(self):
        thread_id = '<pkg-request-' + str(self._reqid) + '@aur.archlinux.org>'
        headers = headers_reply(thread_id)
        headers.update(headers_cc(self._cc))
        return headers


class TUVoteReminderNotification(Notification):
    def __init__(self, conn, vote_id):
        self._vote_id = int(vote_id)
Lukas Fleischer's avatar
Lukas Fleischer committed
502
        cur = conn.execute('SELECT Email, LangPreference FROM Users ' +
503
504
505
                           'WHERE AccountTypeID IN (2, 4) AND ID NOT IN ' +
                           '(SELECT UserID FROM TU_Votes ' +
                           'WHERE TU_Votes.VoteID = ?)', [vote_id])
Lukas Fleischer's avatar
Lukas Fleischer committed
506
507
        self._recipients = cur.fetchall()
        super().__init__()
508
509

    def get_recipients(self):
Lukas Fleischer's avatar
Lukas Fleischer committed
510
        return self._recipients
511

Lukas Fleischer's avatar
Lukas Fleischer committed
512
    def get_subject(self, lang):
513
514
        return self._l10n.translate('TU Vote Reminder: Proposal {id}',
                                    lang).format(id=self._vote_id)
515

Lukas Fleischer's avatar
Lukas Fleischer committed
516
517
    def get_body(self, lang):
        return self._l10n.translate(
518
519
520
                'Please remember to cast your vote on proposal {id} [1]. '
                'The voting period ends in less than 48 hours.',
                lang).format(id=self._vote_id)
521
522
523

    def get_refs(self):
        return (aur_location + '/tu/?id=' + str(self._vote_id),)
524
525


526
def main():
527
528
    action = sys.argv[1]
    action_map = {
529
530
531
532
533
534
535
536
537
538
539
540
541
        'send-resetkey': ResetKeyNotification,
        'welcome': WelcomeNotification,
        'comment': CommentNotification,
        'update': UpdateNotification,
        'flag': FlagNotification,
        'adopt': AdoptNotification,
        'disown': DisownNotification,
        'comaintainer-add': ComaintainerAddNotification,
        'comaintainer-remove': ComaintainerRemoveNotification,
        'delete': DeleteNotification,
        'request-open': RequestOpenNotification,
        'request-close': RequestCloseNotification,
        'tu-vote-reminder': TUVoteReminderNotification,
542
543
    }

Lukas Fleischer's avatar
Lukas Fleischer committed
544
    conn = aurweb.db.Connection()
545

546
547
    notification = action_map[action](conn, *sys.argv[2:])
    notification.send()
548

Lukas Fleischer's avatar
Lukas Fleischer committed
549
550
    conn.commit()
    conn.close()
551
552
553
554


if __name__ == '__main__':
    main()