#!/usr/bin/env python3 # -*- coding: utf-8 -*- # #### Assemble the Undead ##### # # This is a script that serves the purpose of creating a wikipage and # send out a mail, when there are issues in redmine with a certain # status. It's supposed to be running as a crownjob. # #### Arguments: ##### # # --nomail do not send a mail # --nowiki don't create a wiki page # --verbose, -v output everything # --quiet, -q only print errors # # #### Copyright 2020 ##### # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. # # import sys, requests, datetime, smtplib from email.mime.text import MIMEText from email.utils import formatdate # Redmine config redmine_url = 'https://tickets.zom.bi' redmine_api_key = '' ## Mediawiki config mediawiki_url = 'https://w.zom.bi' # should be Bot credentials, that can be created on Special:BotPasswords mediawiki_username = 'Assemblybot@assemblybot' mediawiki_botpassword='' # smtp config smtp_host = 'mail.zom.bi' smtp_port = 465 smtp_from = 'assembly_noreply@zom.bi' smtp_recipients = [['']] smtp_user = 'assemblybot' smtp_password = '' mail_subject = '☣ The undead assemble' mail_header = "Greetings fellow undead,\nthere are some open \ issues that require a decision from an assembly. You'll find a list \ of said issues at the end of this mail. If you wonna know more about \ those topics, please head to our issue tracker at https://tickets.zom.bi\ \n----\n\n\n" mail_footer = "\n----\n beep, boop. I'm a bot.\n If you wonna\ complain about me, write a mail to cpp or create a ticket at https://tickets.zom.bi" def main(args): assembly_date = str(datetime.date.today() +\ datetime.timedelta((6-datetime.date.today().weekday()) % 7)) requires_assembly = redmine_get_requires_assembly_id() log(1,"Assembly topics:\n") issues = redmine_get_issues(requires_assembly) if not issues: # do nothing, if there are no issues requiring an assembly. log(1,"No issue requires an assembly.\n exiting.") return 0 mediawiki_page = {'title': 'Zombi:Assembly '+assembly_date,\ 'content':'{{plenum|Datum=' + assembly_date + '}}'} mediawiki_page['content'] = '=Issues from redmine that require an assembly=\n' for issue in issues: # Append every issue to the page content log(1,issue['subject']) mediawiki_page['content']= mediawiki_page['content'] + '==' +\ issue["subject"]+ '==\n\n' + 'Author: ' +\ issue['author']['name'] + '\n\n' + issue['description'] + '\n' log(1,'') if '--no-wiki' in args: log(1,"'--no-wiki' argument given, so no wiki page has been created.") else: mediawiki_session = mediawiki_login() mediawiki_create(mediawiki_session, mediawiki_page) # Send out the mail if '--no-mail' in args: log(1,"'--no-mail' argument given, so no mails have been sent.") else: smtp_send() return 0 def smtp_send() smtp_server = smtplib.SMTP_SSL(host=smtp_host,port=smtp_port) if loglevel == 2: smtp_server.set_debuglevel(1) smtp_server.connect(host=smtp_host) smtp_server.login(user=smtp_user,password=smtp_password) mail_message = MIMEText(mail_header + \ mediawiki_page['content'] + mail_footer) mail_message['Subject'] = mail_subject mail_message['From'] = smtp_from mail_message['Date'] = formatdate() for recipient in smtp_recipients: mail_message['To'] = recipient smtp_server.sendmail(from_addr=smtp_from,\ to_addrs=recipient,msg=mail_message.as_string()) smtp_server.quit() def redmine_request(path): request = requests.get(redmine_url + path, headers={ 'X-Redmine-API-Key': redmine_api_key } ) return request.json() def redmine_get_requires_assembly_id(): data = redmine_request('/issue_statuses.json') requires_assembly = next(filter(lambda x: x['name'] == \ 'Requires Assembly',data['issue_statuses']))['id'] return requires_assembly def redmine_get_issues(state): tickets = redmine_request('/issues.json?status_id=' + str(state)) return tickets['issues'] def mediawiki_login(): log(2,"Logging into mediawiki") s = requests.Session() t_request = s.post(mediawiki_url + "/api.php?action=query&format=json&meta=tokens&type=login") token = t_request.json()["query"]["tokens"]["logintoken"] response= s.post(mediawiki_url + '/api.php?action=login&format=json', data={ 'lgname': mediawiki_username, 'lgpassword': mediawiki_botpassword, 'lgtoken': token } ).json() if 'error' in response: log(0,'Logging into mediawiki failed, errorcode: '+ response['error']['code']) raise ValueError(response["error"]) else: log(2,response['login']['result']) return s def mediawiki_create(s,page): log(2,'Creating Wiki page "'+ page['title'] + '"') t_request = s.post(mediawiki_url + '/api.php?action=query&format=json&meta=tokens') token = t_request.json()["query"]["tokens"]["csrftoken"] response = s.post(mediawiki_url + '/api.php?action=edit&format=json' \ + '&title=' + page['title'] + '&text=', data = { 'token': token, 'text' : page['content'] } ).json() if 'error' in response: log(0,'Creating page failed, errorcode: '+ response['error']['code']) raise ValueError(response["error"]) else: log(2,response['edit']['result']) return 0 def log(l,log_message): # 0 - only print errors (-q) # 1 - normal operation # 2 - print everything if l<=loglevel: print(log_message) if __name__ == '__main__': if '-q' in sys.argv or '--quiet' in sys.argv: loglevel = 0 elif '-v' in sys.argv or '--verbose' in sys.argv: loglevel = 2 else: loglevel = 1 sys.exit(main(sys.argv))