Refactor the notification script

Reimplement most of the notification script logic. Create a separate
class for each notification type. Each class provides methods for
generating the list of recipients, the message subject, the message
body, the references to add at the end of the message and the message
headers. Additionally, a method for sending notification emails is
provided.

One major benefit of the new implementation is that both the generation
of recipients and message contents are much more flexible. For example,
it is now easily possible to make user-specific adjustments to every
single notification of a batch.

Signed-off-by: Lukas Fleischer <lfleischer@archlinux.org>
This commit is contained in:
Lukas Fleischer 2018-05-17 19:49:34 +02:00
parent fec253a65d
commit f3b4c5c6bc

View file

@ -9,11 +9,6 @@ import aurweb.config
import aurweb.db import aurweb.db
aur_location = aurweb.config.get('options', 'aur_location') aur_location = aurweb.config.get('options', 'aur_location')
aur_request_ml = aurweb.config.get('options', 'aur_request_ml')
sendmail = aurweb.config.get('notifications', 'sendmail')
sender = aurweb.config.get('notifications', 'sender')
reply_to = aurweb.config.get('notifications', 'reply-to')
def headers_cc(cclist): def headers_cc(cclist):
@ -28,29 +23,6 @@ def headers_reply(thread_id):
return {'In-Reply-To': thread_id, 'References': thread_id} return {'In-Reply-To': thread_id, 'References': thread_id}
def send_notification(to, subject, body, refs, headers={}):
wrapped = ''
for line in body.splitlines():
wrapped += textwrap.fill(line, break_long_words=False) + '\n'
if refs:
body = wrapped + '\n' + refs
else:
body = wrapped.rstrip()
for recipient in to:
msg = email.mime.text.MIMEText(body, 'plain', 'utf-8')
msg['Subject'] = subject
msg['From'] = sender
msg['Reply-to'] = reply_to
msg['To'] = recipient
for key, value in headers.items():
msg[key] = value
p = subprocess.Popen([sendmail, '-t', '-oi'], stdin=subprocess.PIPE)
p.communicate(msg.as_bytes())
def username_from_id(conn, uid): def username_from_id(conn, uid):
cur = conn.execute('SELECT UserName FROM Users WHERE ID = ?', [uid]) cur = conn.execute('SELECT UserName FROM Users WHERE ID = ?', [uid])
return cur.fetchone()[0] return cur.fetchone()[0]
@ -73,380 +45,444 @@ def get_user_email(conn, uid):
return cur.fetchone()[0] return cur.fetchone()[0]
def get_flag_recipients(conn, pkgbase_id): class Notification:
cur = conn.execute('SELECT DISTINCT Users.Email FROM Users ' + def get_refs(self):
'LEFT JOIN PackageComaintainers ' + return ()
'ON PackageComaintainers.UsersID = Users.ID ' +
'INNER JOIN PackageBases ' + def get_headers(self):
'ON PackageBases.MaintainerUID = Users.ID OR ' + return {}
'PackageBases.ID = PackageComaintainers.PackageBaseID ' +
'WHERE PackageBases.ID = ?', [pkgbase_id]) def send(self):
return [row[0] for row in cur.fetchall()] body = ''
for line in self.get_body().splitlines():
body += textwrap.fill(line, break_long_words=False) + '\n'
def get_recipients(conn, pkgbase_id, uid): for i, ref in enumerate(self.get_refs()):
cur = conn.execute('SELECT DISTINCT Users.Email FROM Users ' + body += '\n' + '[%d] %s' % (i + 1, ref)
'INNER JOIN PackageNotifications ' + body = body.rstrip()
'ON PackageNotifications.UserID = Users.ID WHERE ' +
'PackageNotifications.UserID != ? AND ' + sendmail = aurweb.config.get('notifications', 'sendmail')
'PackageNotifications.PackageBaseID = ?', sender = aurweb.config.get('notifications', 'sender')
[uid, pkgbase_id]) reply_to = aurweb.config.get('notifications', 'reply-to')
return [row[0] for row in cur.fetchall()]
for recipient in self.get_recipients():
msg = email.mime.text.MIMEText(body, 'plain', 'utf-8')
def get_comment_recipients(conn, pkgbase_id, uid): msg['Subject'] = self.get_subject()
cur = conn.execute('SELECT DISTINCT Users.Email FROM Users ' + msg['From'] = sender
'INNER JOIN PackageNotifications ' + msg['Reply-to'] = reply_to
'ON PackageNotifications.UserID = Users.ID WHERE ' + msg['To'] = recipient
'Users.CommentNotify = 1 AND ' +
'PackageNotifications.UserID != ? AND ' + for key, value in self.get_headers().items():
'PackageNotifications.PackageBaseID = ?', msg[key] = value
[uid, pkgbase_id])
return [row[0] for row in cur.fetchall()] p = subprocess.Popen([sendmail, '-t', '-oi'],
stdin=subprocess.PIPE)
p.communicate(msg.as_bytes())
def get_update_recipients(conn, pkgbase_id, uid):
cur = conn.execute('SELECT DISTINCT Users.Email FROM Users ' +
'INNER JOIN PackageNotifications ' + class ResetKeyNotification(Notification):
'ON PackageNotifications.UserID = Users.ID WHERE ' + def __init__(self, conn, uid):
'Users.UpdateNotify = 1 AND ' + cur = conn.execute('SELECT UserName, Email, ResetKey FROM Users ' +
'PackageNotifications.UserID != ? AND ' + 'WHERE ID = ?', [uid])
'PackageNotifications.PackageBaseID = ?', self._username, self._to, self._resetkey = cur.fetchone()
[uid, pkgbase_id])
return [row[0] for row in cur.fetchall()] def get_recipients(self):
return [self._to]
def get_ownership_recipients(conn, pkgbase_id, uid): def get_subject(self):
cur = conn.execute('SELECT DISTINCT Users.Email FROM Users ' + return 'AUR Password Reset'
'INNER JOIN PackageNotifications ' +
'ON PackageNotifications.UserID = Users.ID WHERE ' + def get_body(self):
'Users.OwnershipNotify = 1 AND ' + return 'A password reset request was submitted for the account %s ' \
'PackageNotifications.UserID != ? AND ' + 'associated with your email address. If you wish to reset ' \
'PackageNotifications.PackageBaseID = ?', 'your password follow the link [1] below, otherwise ignore ' \
[uid, pkgbase_id]) 'this message and nothing will happen.' % (self._username)
return [row[0] for row in cur.fetchall()]
def get_refs(self):
return (aur_location + '/passreset/?resetkey=' + self._resetkey,)
def get_request_recipients(conn, reqid):
cur = conn.execute('SELECT DISTINCT Users.Email FROM PackageRequests ' +
'INNER JOIN PackageBases ' + class WelcomeNotification(ResetKeyNotification):
'ON PackageBases.ID = PackageRequests.PackageBaseID ' + def get_subject(self):
'INNER JOIN Users ' + return 'Welcome to the Arch User Repository'
'ON Users.ID = PackageRequests.UsersID ' +
'OR Users.ID = PackageBases.MaintainerUID ' + def get_body(self):
'WHERE PackageRequests.ID = ?', [reqid]) return 'Welcome to the Arch User Repository! In order to set an ' \
return [row[0] for row in cur.fetchall()] '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.'
def get_tu_vote_reminder_recipients(conn, vote_id):
cur = conn.execute('SELECT Email FROM Users ' +
'WHERE AccountTypeID IN (2, 4) AND ID NOT IN ' + class CommentNotification(Notification):
'(SELECT UserID FROM TU_Votes ' + def __init__(self, conn, uid, pkgbase_id, comment_id):
'WHERE TU_Votes.VoteID = ?)', [vote_id]) self._user = username_from_id(conn, uid)
return [row[0] for row in cur.fetchall()] self._pkgbase = pkgbase_from_id(conn, pkgbase_id)
cur = conn.execute('SELECT DISTINCT Users.Email FROM Users ' +
'INNER JOIN PackageNotifications ' +
def get_comment(conn, comment_id): 'ON PackageNotifications.UserID = Users.ID WHERE ' +
cur = conn.execute('SELECT Comments FROM PackageComments WHERE ID = ?', 'Users.CommentNotify = 1 AND ' +
[comment_id]) 'PackageNotifications.UserID != ? AND ' +
return cur.fetchone()[0] 'PackageNotifications.PackageBaseID = ?',
[uid, pkgbase_id])
self._to = [row[0] for row in cur.fetchall()]
def get_flagger_comment(conn, pkgbase_id): cur = conn.execute('SELECT Comments FROM PackageComments WHERE ID = ?',
cur = conn.execute('SELECT FlaggerComment FROM PackageBases WHERE ID = ?', [comment_id])
[pkgbase_id]) self._text = cur.fetchone()[0]
return cur.fetchone()[0]
def get_recipients(self):
return self._to
def get_request_comment(conn, reqid):
cur = conn.execute('SELECT Comments FROM PackageRequests WHERE ID = ?', def get_subject(self):
[reqid]) return 'AUR Comment for %s' % (self._pkgbase)
return cur.fetchone()[0]
def get_body(self):
body = '%s [1] added the following comment to %s [2]:' % \
def get_request_closure_comment(conn, reqid): (self._user, self._pkgbase)
cur = conn.execute('SELECT ClosureComment FROM PackageRequests ' + body += '\n\n' + self._text + '\n\n'
'WHERE ID = ?', [reqid]) body += 'If you no longer wish to receive notifications about this ' \
return cur.fetchone()[0] 'package, please go to the package page [2] and select ' \
'"%s".' % ('Disable notifications')
return body
def send_resetkey(conn, uid):
cur = conn.execute('SELECT UserName, Email, ResetKey FROM Users ' + def get_refs(self):
'WHERE ID = ?', [uid]) return (aur_location + '/account/' + self._user + '/',
username, to, resetkey = cur.fetchone() aur_location + '/pkgbase/' + self._pkgbase + '/')
subject = 'AUR Password Reset' def get_headers(self):
body = 'A password reset request was submitted for the account %s ' \ thread_id = '<pkg-notifications-' + self._pkgbase + \
'associated with your email address. If you wish to reset your ' \ '@aur.archlinux.org>'
'password follow the link [1] below, otherwise ignore this ' \ return headers_reply(thread_id)
'message and nothing will happen.' % (username)
refs = '[1] ' + aur_location + '/passreset/?resetkey=' + resetkey
class UpdateNotification(Notification):
send_notification([to], subject, body, refs) def __init__(self, conn, uid, pkgbase_id):
self._user = username_from_id(conn, uid)
self._pkgbase = pkgbase_from_id(conn, pkgbase_id)
def welcome(conn, uid): cur = conn.execute('SELECT DISTINCT Users.Email FROM Users ' +
cur = conn.execute('SELECT UserName, Email, ResetKey FROM Users ' + 'INNER JOIN PackageNotifications ' +
'WHERE ID = ?', [uid]) 'ON PackageNotifications.UserID = Users.ID WHERE ' +
username, to, resetkey = cur.fetchone() 'Users.UpdateNotify = 1 AND ' +
'PackageNotifications.UserID != ? AND ' +
subject = 'Welcome to the Arch User Repository' 'PackageNotifications.PackageBaseID = ?',
body = 'Welcome to the Arch User Repository! In order to set an initial ' \ [uid, pkgbase_id])
'password for your new account, please click the link [1] below. ' \ self._to = [row[0] for row in cur.fetchall()]
'If the link does not work, try copying and pasting it into your ' \
'browser.' def get_recipients(self):
refs = '[1] ' + aur_location + '/passreset/?resetkey=' + resetkey return self._to
send_notification([to], subject, body, refs) def get_subject(self):
return 'AUR Package Update: %s' % (self._pkgbase)
def comment(conn, uid, pkgbase_id, comment_id): def get_body(self):
user = username_from_id(conn, uid) body = '%s [1] pushed a new commit to %s [2].' % \
pkgbase = pkgbase_from_id(conn, pkgbase_id) (self._user, self._pkgbase)
to = get_comment_recipients(conn, pkgbase_id, uid) body += '\n\n'
text = get_comment(conn, comment_id) body += 'If you no longer wish to receive notifications about this ' \
'package, please go to the package page [2] and select ' \
user_uri = aur_location + '/account/' + user + '/' '"%s".' % ('Disable notifications')
pkgbase_uri = aur_location + '/pkgbase/' + pkgbase + '/' return body
subject = 'AUR Comment for %s' % (pkgbase) def get_refs(self):
body = '%s [1] added the following comment to %s [2]:' % (user, pkgbase) return (aur_location + '/account/' + self._user + '/',
body += '\n\n' + text + '\n\n' aur_location + '/pkgbase/' + self._pkgbase + '/')
body += 'If you no longer wish to receive notifications about this ' \
'package, please go to the package page [2] and select "%s".' % \ def get_headers(self):
('Disable notifications') thread_id = '<pkg-notifications-' + self._pkgbase + \
refs = '[1] ' + user_uri + '\n' '@aur.archlinux.org>'
refs += '[2] ' + pkgbase_uri return headers_reply(thread_id)
thread_id = '<pkg-notifications-' + pkgbase + '@aur.archlinux.org>'
headers = headers_reply(thread_id)
class FlagNotification(Notification):
send_notification(to, subject, body, refs, headers) def __init__(self, conn, uid, pkgbase_id):
self._user = username_from_id(conn, uid)
self._pkgbase = pkgbase_from_id(conn, pkgbase_id)
def update(conn, uid, pkgbase_id): cur = conn.execute('SELECT DISTINCT Users.Email FROM Users ' +
user = username_from_id(conn, uid) 'LEFT JOIN PackageComaintainers ' +
pkgbase = pkgbase_from_id(conn, pkgbase_id) 'ON PackageComaintainers.UsersID = Users.ID ' +
to = get_update_recipients(conn, pkgbase_id, uid) 'INNER JOIN PackageBases ' +
'ON PackageBases.MaintainerUID = Users.ID OR ' +
user_uri = aur_location + '/account/' + user + '/' 'PackageBases.ID = PackageComaintainers.PackageBaseID ' +
pkgbase_uri = aur_location + '/pkgbase/' + pkgbase + '/' 'WHERE PackageBases.ID = ?', [pkgbase_id])
self._to = [row[0] for row in cur.fetchall()]
subject = 'AUR Package Update: %s' % (pkgbase) cur = conn.execute('SELECT FlaggerComment FROM PackageBases WHERE ' +
body = '%s [1] pushed a new commit to %s [2].' % (user, pkgbase) 'ID = ?', [pkgbase_id])
body += '\n\n' self._text = cur.fetchone()[0]
body += 'If you no longer wish to receive notifications about this ' \
'package, please go to the package page [2] and select "%s".' % \ def get_recipients(self):
('Disable notifications') return self._to
refs = '[1] ' + user_uri + '\n'
refs += '[2] ' + pkgbase_uri def get_subject(self):
thread_id = '<pkg-notifications-' + pkgbase + '@aur.archlinux.org>' return 'AUR Out-of-date Notification for %s' % (self._pkgbase)
headers = headers_reply(thread_id)
def get_body(self):
send_notification(to, subject, body, refs, headers) body = 'Your package %s [1] has been flagged out-of-date by ' \
'%s [2]:' % (self._pkgbase, self._user)
body += '\n\n' + self._text
def flag(conn, uid, pkgbase_id): return body
user = username_from_id(conn, uid)
pkgbase = pkgbase_from_id(conn, pkgbase_id) def get_refs(self):
to = get_flag_recipients(conn, pkgbase_id) return (aur_location + '/pkgbase/' + self._pkgbase + '/',
text = get_flagger_comment(conn, pkgbase_id) aur_location + '/account/' + self._user + '/')
user_uri = aur_location + '/account/' + user + '/'
pkgbase_uri = aur_location + '/pkgbase/' + pkgbase + '/' class OwnershipEventNotification(Notification):
def __init__(self, conn, uid, pkgbase_id):
subject = 'AUR Out-of-date Notification for %s' % (pkgbase) self._user = username_from_id(conn, uid)
body = 'Your package %s [1] has been flagged out-of-date by %s [2]:' % \ self._pkgbase = pkgbase_from_id(conn, pkgbase_id)
(pkgbase, user) cur = conn.execute('SELECT DISTINCT Users.Email FROM Users ' +
body += '\n\n' + text 'INNER JOIN PackageNotifications ' +
refs = '[1] ' + pkgbase_uri + '\n' 'ON PackageNotifications.UserID = Users.ID WHERE ' +
refs += '[2] ' + user_uri 'Users.OwnershipNotify = 1 AND ' +
'PackageNotifications.UserID != ? AND ' +
send_notification(to, subject, body, refs) 'PackageNotifications.PackageBaseID = ?',
[uid, pkgbase_id])
self._to = [row[0] for row in cur.fetchall()]
def adopt(conn, pkgbase_id, uid): cur = conn.execute('SELECT FlaggerComment FROM PackageBases WHERE ' +
user = username_from_id(conn, uid) 'ID = ?', [pkgbase_id])
pkgbase = pkgbase_from_id(conn, pkgbase_id) self._text = cur.fetchone()[0]
to = get_ownership_recipients(conn, pkgbase_id, uid)
def get_recipients(self):
user_uri = aur_location + '/account/' + user + '/' return self._to
pkgbase_uri = aur_location + '/pkgbase/' + pkgbase + '/'
def get_subject(self):
subject = 'AUR Ownership Notification for %s' % (pkgbase) return 'AUR Ownership Notification for %s' % (self._pkgbase)
body = 'The package %s [1] was adopted by %s [2].' % (pkgbase, user)
refs = '[1] ' + pkgbase_uri + '\n' def get_refs(self):
refs += '[2] ' + user_uri return (aur_location + '/pkgbase/' + self._pkgbase + '/',
aur_location + '/account/' + self._user + '/')
send_notification(to, subject, body, refs)
class AdoptNotification(OwnershipEventNotification):
def disown(conn, pkgbase_id, uid): def get_body(self):
user = username_from_id(conn, uid) return 'The package %s [1] was adopted by %s [2].' % \
pkgbase = pkgbase_from_id(conn, pkgbase_id) (self._pkgbase, self._user)
to = get_ownership_recipients(conn, pkgbase_id, uid)
user_uri = aur_location + '/account/' + user + '/' class DisownNotification(OwnershipEventNotification):
pkgbase_uri = aur_location + '/pkgbase/' + pkgbase + '/' def get_body(self):
return 'The package %s [1] was disowned by %s [2].' % \
subject = 'AUR Ownership Notification for %s' % (pkgbase) (self._pkgbase, self._user)
body = 'The package %s [1] was disowned by %s [2].' % (pkgbase, user)
refs = '[1] ' + pkgbase_uri + '\n'
refs += '[2] ' + user_uri class ComaintainershipEventNotification(Notification):
def __init__(self, conn, uid, pkgbase_id):
send_notification(to, subject, body, refs) self._pkgbase = pkgbase_from_id(conn, pkgbase_id)
self._to = get_user_email(conn, uid)
def comaintainer_add(conn, pkgbase_id, uid): def get_recipients(self):
pkgbase = pkgbase_from_id(conn, pkgbase_id) return [self._to]
to = [get_user_email(conn, uid)]
def get_subject(self):
pkgbase_uri = aur_location + '/pkgbase/' + pkgbase + '/' return 'AUR Co-Maintainer Notification for %s' % (self._pkgbase)
subject = 'AUR Co-Maintainer Notification for %s' % (pkgbase) def get_refs(self):
body = 'You were added to the co-maintainer list of %s [1].' % (pkgbase) return (aur_location + '/pkgbase/' + self._pkgbase + '/',)
refs = '[1] ' + pkgbase_uri
send_notification(to, subject, body, refs) class ComaintainerAddNotification(ComaintainershipEventNotification):
def get_body(self):
return 'You were added to the co-maintainer list of %s [1].' % \
def comaintainer_remove(conn, pkgbase_id, uid): (self._pkgbase)
pkgbase = pkgbase_from_id(conn, pkgbase_id)
to = [get_user_email(conn, uid)]
class ComaintainerRemoveNotification(ComaintainershipEventNotification):
pkgbase_uri = aur_location + '/pkgbase/' + pkgbase + '/' def get_body(self):
return 'You were removed from the co-maintainer list of %s [1].' % \
subject = 'AUR Co-Maintainer Notification for %s' % (pkgbase) (self._pkgbase)
body = ('You were removed from the co-maintainer list of %s [1].' %
(pkgbase))
refs = '[1] ' + pkgbase_uri class DeleteNotification(Notification):
def __init__(self, conn, uid, old_pkgbase_id, new_pkgbase_id=None):
send_notification(to, subject, body, refs) self._user = username_from_id(conn, uid)
self._old_pkgbase = pkgbase_from_id(conn, old_pkgbase_id)
if new_pkgbase_id:
def delete(conn, uid, old_pkgbase_id, new_pkgbase_id=None): self._new_pkgbase = pkgbase_from_id(conn, new_pkgbase_id)
user = username_from_id(conn, uid) else:
old_pkgbase = pkgbase_from_id(conn, old_pkgbase_id) self._new_pkgbase = None
if new_pkgbase_id: cur = conn.execute('SELECT DISTINCT Users.Email FROM Users ' +
new_pkgbase = pkgbase_from_id(conn, new_pkgbase_id) 'INNER JOIN PackageNotifications ' +
to = get_recipients(conn, old_pkgbase_id, uid) 'ON PackageNotifications.UserID = Users.ID WHERE ' +
'PackageNotifications.UserID != ? AND ' +
user_uri = aur_location + '/account/' + user + '/' 'PackageNotifications.PackageBaseID = ?',
pkgbase_uri = aur_location + '/pkgbase/' + old_pkgbase + '/' [uid, old_pkgbase_id])
self._to = [row[0] for row in cur.fetchall()]
subject = 'AUR Package deleted: %s' % (old_pkgbase)
if new_pkgbase_id: def get_recipients(self):
new_pkgbase_uri = aur_location + '/pkgbase/' + new_pkgbase + '/' return self._to
body = '%s [1] merged %s [2] into %s [3].\n\n' \
'If you no longer wish receive notifications about the new ' \ def get_subject(self):
'package, please go to [3] and click "%s".' %\ return 'AUR Package deleted: %s' % (self._old_pkgbase)
(user, old_pkgbase, new_pkgbase, 'Disable notifications')
refs = '[1] ' + user_uri + '\n' def get_body(self):
refs += '[2] ' + pkgbase_uri + '\n' if self._new_pkgbase:
refs += '[3] ' + new_pkgbase_uri return '%s [1] merged %s [2] into %s [3].\n\n' \
else: 'If you no longer wish receive notifications about the ' \
body = '%s [1] deleted %s [2].\n\n' \ 'new package, please go to [3] and click "%s".' % \
'You will no longer receive notifications about this ' \ (self._user, self._old_pkgbase, self._new_pkgbase,
'package.' % (user, old_pkgbase) 'Disable notifications')
refs = '[1] ' + user_uri + '\n' else:
refs += '[2] ' + pkgbase_uri return '%s [1] deleted %s [2].\n\n' \
'You will no longer receive notifications about this ' \
send_notification(to, subject, body, refs) 'package.' % (self._user, self._old_pkgbase)
def get_refs(self):
def request_open(conn, uid, reqid, reqtype, pkgbase_id, merge_into=None): refs = (aur_location + '/account/' + self._user + '/',
user = username_from_id(conn, uid) aur_location + '/pkgbase/' + self._old_pkgbase + '/')
pkgbase = pkgbase_from_id(conn, pkgbase_id) if self._new_pkgbase:
to = [aur_request_ml] refs += (aur_location + '/pkgbase/' + self._new_pkgbase + '/',)
cc = get_request_recipients(conn, reqid) return refs
text = get_request_comment(conn, reqid)
user_uri = aur_location + '/account/' + user + '/' class RequestOpenNotification(Notification):
pkgbase_uri = aur_location + '/pkgbase/' + pkgbase + '/' def __init__(self, conn, uid, reqid, reqtype, pkgbase_id, merge_into=None):
self._user = username_from_id(conn, uid)
subject = '[PRQ#%d] %s Request for %s' % \ self._pkgbase = pkgbase_from_id(conn, pkgbase_id)
(int(reqid), reqtype.title(), pkgbase) cur = conn.execute('SELECT DISTINCT Users.Email FROM PackageRequests ' +
if merge_into: 'INNER JOIN PackageBases ' +
merge_into_uri = aur_location + '/pkgbase/' + merge_into + '/' 'ON PackageBases.ID = PackageRequests.PackageBaseID ' +
body = '%s [1] filed a request to merge %s [2] into %s [3]:' % \ 'INNER JOIN Users ' +
(user, pkgbase, merge_into) 'ON Users.ID = PackageRequests.UsersID ' +
body += '\n\n' + text 'OR Users.ID = PackageBases.MaintainerUID ' +
refs = '[1] ' + user_uri + '\n' 'WHERE PackageRequests.ID = ?', [reqid])
refs += '[2] ' + pkgbase_uri + '\n' self._to = aurweb.config.get('options', 'aur_request_ml')
refs += '[3] ' + merge_into_uri self._cc = [row[0] for row in cur.fetchall()]
else: cur = conn.execute('SELECT Comments FROM PackageRequests WHERE ID = ?',
body = '%s [1] filed a %s request for %s [2]:' % \ [reqid])
(user, reqtype, pkgbase) self._text = cur.fetchone()[0]
body += '\n\n' + text self._reqid = int(reqid)
refs = '[1] ' + user_uri + '\n' self._reqtype = reqtype
refs += '[2] ' + pkgbase_uri self._merge_into = merge_into
thread_id = '<pkg-request-' + reqid + '@aur.archlinux.org>'
# Use a deterministic Message-ID for the first email referencing a request. def get_recipients(self):
headers = headers_msgid(thread_id) return [self._to]
headers.update(headers_cc(cc))
def get_subject(self):
send_notification(to, subject, body, refs, headers) return '[PRQ#%d] %s Request for %s' % \
(self._reqid, self._reqtype.title(), self._pkgbase)
def request_close(conn, uid, reqid, reason): def get_body(self):
to = [aur_request_ml] if self._merge_into:
cc = get_request_recipients(conn, reqid) body = '%s [1] filed a request to merge %s [2] into %s [3]:' % \
text = get_request_closure_comment(conn, reqid) (self._user, self._pkgbase, self._merge_into)
body += '\n\n' + self._text
subject = '[PRQ#%d] Request %s' % (int(reqid), reason.title()) else:
if int(uid): body = '%s [1] filed a %s request for %s [2]:' % \
user = username_from_id(conn, uid) (self._user, self._reqtype, self._pkgbase)
user_uri = aur_location + '/account/' + user + '/' body += '\n\n' + self._text
body = 'Request #%d has been %s by %s [1]' % (int(reqid), reason, user) return body
refs = '[1] ' + user_uri
else: def get_refs(self):
body = 'Request #%d has been %s automatically by the Arch User ' \ refs = (aur_location + '/account/' + self._user + '/',
'Repository package request system' % (int(reqid), reason) aur_location + '/pkgbase/' + self._pkgbase + '/')
refs = None if self._merge_into:
if text.strip() == '': refs += (aur_location + '/pkgbase/' + self._merge_into + '/',)
body += '.' return refs
else:
body += ':\n\n' + text def get_headers(self):
thread_id = '<pkg-request-' + reqid + '@aur.archlinux.org>' thread_id = '<pkg-request-' + str(self._reqid) + '@aur.archlinux.org>'
headers = headers_reply(thread_id) # Use a deterministic Message-ID for the first email referencing a
headers.update(headers_cc(cc)) # request.
headers = headers_msgid(thread_id)
send_notification(to, subject, body, refs, headers) headers.update(headers_cc(self._cc))
return headers
def tu_vote_reminder(conn, vote_id):
to = get_tu_vote_reminder_recipients(conn, vote_id) class RequestCloseNotification(Notification):
def __init__(self, conn, uid, reqid, reason):
vote_uri = aur_location + '/tu/?id=' + vote_id self._user = username_from_id(conn, uid) if int(uid) else None
cur = conn.execute('SELECT DISTINCT Users.Email FROM PackageRequests ' +
subject = 'TU Vote Reminder: Proposal %d' % (int(vote_id)) 'INNER JOIN PackageBases ' +
body = 'Please remember to cast your vote on proposal %d [1]. ' \ 'ON PackageBases.ID = PackageRequests.PackageBaseID ' +
'The voting period ends in less than 48 hours.' % (int(vote_id)) 'INNER JOIN Users ' +
refs = '[1] ' + vote_uri 'ON Users.ID = PackageRequests.UsersID ' +
'OR Users.ID = PackageBases.MaintainerUID ' +
send_notification(to, subject, body, refs) '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 ClosureComment FROM PackageRequests ' +
'WHERE ID = ?', [reqid])
self._text = cur.fetchone()[0]
self._reqid = int(reqid)
self._reason = reason
def get_recipients(self):
return [self._to]
def get_subject(self):
return '[PRQ#%d] Request %s' % (self._reqid, self._reason.title())
def get_body(self):
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)
cur = conn.execute('SELECT Email FROM Users ' +
'WHERE AccountTypeID IN (2, 4) AND ID NOT IN ' +
'(SELECT UserID FROM TU_Votes ' +
'WHERE TU_Votes.VoteID = ?)', [vote_id])
self._to = [row[0] for row in cur.fetchall()]
def get_recipients(self):
return self._to
def get_subject(self):
return 'TU Vote Reminder: Proposal %d' % (self._vote_id)
def get_body(self):
return 'Please remember to cast your vote on proposal %d [1]. ' \
'The voting period ends in less than 48 hours.' % \
(self._vote_id)
def get_refs(self):
return (aur_location + '/tu/?id=' + str(self._vote_id),)
def main(): def main():
action = sys.argv[1] action = sys.argv[1]
action_map = { action_map = {
'send-resetkey': send_resetkey, 'send-resetkey': ResetKeyNotification,
'welcome': welcome, 'welcome': WelcomeNotification,
'comment': comment, 'comment': CommentNotification,
'update': update, 'update': UpdateNotification,
'flag': flag, 'flag': FlagNotification,
'adopt': adopt, 'adopt': AdoptNotification,
'disown': disown, 'disown': DisownNotification,
'comaintainer-add': comaintainer_add, 'comaintainer-add': ComaintainerAddNotification,
'comaintainer-remove': comaintainer_remove, 'comaintainer-remove': ComaintainerRemoveNotification,
'delete': delete, 'delete': DeleteNotification,
'request-open': request_open, 'request-open': RequestOpenNotification,
'request-close': request_close, 'request-close': RequestCloseNotification,
'tu-vote-reminder': tu_vote_reminder, 'tu-vote-reminder': TUVoteReminderNotification,
} }
conn = aurweb.db.Connection() conn = aurweb.db.Connection()
action_map[action](conn, *sys.argv[2:]) notification = action_map[action](conn, *sys.argv[2:])
notification.send()
conn.commit() conn.commit()
conn.close() conn.close()