#!/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, os, re from email.mime.text import MIMEText from email.utils import formatdate # Redmine config redmine_url = os.getenv('REDMINE_URL') redmine_api_key = os.getenv('REDMINE_API_KEY') ## Mediawiki config mediawiki_url = os.getenv('MEDIAWIKI_URL') # should be Bot credentials, that can be created on Special:BotPasswords mediawiki_username = os.getenv('MEDIAWIKI_USERNAME') mediawiki_botpassword=os.getenv('MEDIAWIKI_BOTPASSWORD') smtp_recipients = re.split(' ',os.getenv('SMTP_RECIPIENTS')) # smtp config smtp_host = os.getenv('SMTP_HOST') smtp_port = os.getenv('SMTP_PORT') smtp_from = os.getenv('SMTP_FROM') smtp_user = os.getenv('SMTP_USER') smtp_password = os.getenv('SMTP_PASSWORD') mail_subject = os.getenv('MAIL_SUBJECT') mail_header = os.getenv('MAIL_HEADER') mail_footer = os.getenv('MAIL_FOOTER') def main(args): assembly_date = str(datetime.date.today() +\ datetime.timedelta((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(mediawiki_page['content']) return 0 def smtp_send(content): 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 + \ 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()) del mail_message['To'] 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))