218 lines
8.1 KiB
Python
218 lines
8.1 KiB
Python
|
|
import sys
|
|
|
|
import os
|
|
import base64
|
|
|
|
import time
|
|
import json
|
|
import re
|
|
import hmac
|
|
import pprint
|
|
import hashlib
|
|
import requests
|
|
from subprocess import call
|
|
from base64 import b64encode
|
|
|
|
from buildbot.process.properties import Properties
|
|
from buildbot.util import bytes2unicode, unicode2bytes
|
|
from buildbot.www.hooks.base import BaseHookHandler
|
|
|
|
from twisted.internet import defer
|
|
from twisted.python import log
|
|
|
|
from dateutil.parser import parse as dateparse
|
|
|
|
_HEADER_USER_AGENT = 'User-Agent'
|
|
_HEADER_SIGNATURE = 'X-TX-Signature'
|
|
_EVENT_KEY = 'event'
|
|
transifexSecret = ""
|
|
transifexUsername = ""
|
|
transifexPassword = ""
|
|
transifex_dict = {}
|
|
secret = ""
|
|
master = ""
|
|
|
|
gitHubToken = os.environ('gitHubToken')
|
|
|
|
class TransifexHandler(BaseHookHandler):
|
|
|
|
def __init__(self, master, secret, transifex_dict):
|
|
self.secret = secret
|
|
self.master = master
|
|
self.transifex_dict = transifex_dict
|
|
|
|
|
|
def returnMessage(self, status = False, message = "Unimplemented"):
|
|
output = json.dumps({"status": "OK" if status else "FAIL", "message": message})
|
|
return [output, [('Content-type', 'text/plain'),
|
|
('Content-Length', str(len(output)))
|
|
]]
|
|
|
|
def verifyTransifexSignature(
|
|
self, request, content, rendered_secret, signature, header_signature
|
|
):
|
|
http_verb = 'POST'
|
|
http_url_path = request.headers('X-TX-Url')
|
|
http_gmt_date = request.headers('Date')
|
|
content_md5 = hashlib.md5(content).hexdigest()
|
|
msg = b'\n'.join([
|
|
http_verb, http_url_path, http_gmt_date, content_md5
|
|
])
|
|
tx_signature = base64.b64encode(
|
|
hmac.new(
|
|
key=rendered_secret,
|
|
msg=msg,
|
|
digestmod=hashlib.sha256
|
|
).digest()
|
|
)
|
|
if tx_signature() != header_signature:
|
|
raise ValueError('Invalid secret')
|
|
|
|
try:
|
|
if signature != os.environ.get('HTTP_X_TX_SIGNATURE'):
|
|
return False
|
|
return True
|
|
except:
|
|
return False
|
|
|
|
def downloadTranslatedLanguage(self, ts, lang):
|
|
headers = {"Authorization": "Basic " + b64encode(transifexUsername + ":" + transifexPassword)}
|
|
fname = "bitmessage_" + lang.lower() + ".po"
|
|
with open("src/translations/" + fname, "wt") as handle:
|
|
response = requests.get("https://www.transifex.com/api/2/project/pybitmessage/resource/pybitmessage/translation/" + lang + "/",
|
|
headers=headers)
|
|
if response.ok:
|
|
content = json.loads(response.content)["content"]
|
|
handle.write(content.encode("utf-8"))
|
|
return response
|
|
|
|
def commitTranslatedLanguage(self, ts, lang):
|
|
call(["kivy", "src/translations/messages.pro"])
|
|
call(["git", "add", "src/translations/bitmessage_" + lang + ".ts", "src/translations/bitmessage_" + lang + ".qm"])
|
|
call(["git", "commit", "-q", "-S", "-m", "Auto-updated language %s from transifex" % (lang)])
|
|
newbranch = "translate_" + lang + "_" + str(ts)
|
|
call(["git", "push", "-q", "translations", newbranch + ":" + newbranch])
|
|
branch = transifex_dict['branch']
|
|
|
|
request = {
|
|
"title": "Translation update " + lang,
|
|
"body": "Auto-updated from transifex",
|
|
"head": "PyBitmessageTranslations:" + newbranch,
|
|
"base": branch
|
|
}
|
|
headers = {"Authorization": "token " + gitHubToken}
|
|
response = requests.post("https://api.github.com/repos/Bitmessage/PyBitmessage/pulls",
|
|
headers=headers, data=json.dumps(request))
|
|
return response
|
|
|
|
|
|
def process_translation_completed(self, payload, transifex_dict, event_type, codebase):
|
|
changes = []
|
|
transifex_response = self._transform_variables(payload, transifex_dict)
|
|
if 'pybitmessage-test' in transifex_response['project'] and 'messagespot' in transifex_response['resource']:
|
|
if 'translation_completed' in transifex_response['event'] and 100 in transifex_response['translated']:
|
|
ts = int(time.time())
|
|
lang = transifex_response['language']
|
|
branch = transifex_dict['branch']
|
|
self.downloadTranslatedLanguage(ts, lang.lower())
|
|
response = self.commitTranslatedLanguage(ts, lang.lower())
|
|
if response.ok:
|
|
output, responseHeaders = self.returnMessage(True, "Processed.")
|
|
else:
|
|
output, responseHeaders = self.returnMessage(False, "Error: %i." % (response.status_code))
|
|
else:
|
|
output, responseHeaders = self.returnMessage(False, "Nothing to do")
|
|
else:
|
|
output, responseHeaders = self.returnMessage(False, "Nothing to do")
|
|
|
|
# if isinstance(self.options, dict) and self.options.get('onlyIncludePushCommit', False):
|
|
# commits = commits[:1]
|
|
|
|
# for commit in commits:
|
|
# files = []
|
|
# for kind in ('added', 'modified', 'removed'):
|
|
# files.extend(commit.get(kind, []) or [])
|
|
# timestamp = dateparse(commit['timestamp'])
|
|
# change = {
|
|
# 'author': '{} <{}>'.format(commit['author']['name'],
|
|
# commit['author']['email']),
|
|
# 'files': files,
|
|
# 'comments': commit['message'],
|
|
# 'revision': commit['id'],
|
|
# 'when_timestamp': timestamp,
|
|
# 'branch': branch,
|
|
# 'revlink': commit['url'],
|
|
# 'repository': repo_url,
|
|
# 'project': project,
|
|
# 'category': event_type,
|
|
# 'properties': {
|
|
# 'event': event_type,
|
|
# 'repository_name': repository['name'],
|
|
# 'owner': repository["owner"]["username"]
|
|
# },
|
|
# }
|
|
# if codebase is not None:
|
|
# change['codebase'] = codebase
|
|
# changes.insert(0, change)
|
|
return changes
|
|
|
|
def process_review_completed(self, payload, transifex_data):
|
|
pass
|
|
|
|
|
|
def _transform_variables(self, payload, transifex_dict):
|
|
transifex_variables = {
|
|
'project': payload['project'],
|
|
"translated": payload['translated'],
|
|
"resource": payload['resource'],
|
|
"event": payload['event'],
|
|
"language": payload['language']
|
|
}
|
|
|
|
return transifex_variables
|
|
|
|
@defer.inlineCallbacks
|
|
def getChanges(self, request):
|
|
self.secret = None
|
|
if isinstance(self.options, dict):
|
|
self.secret = self.options.get('secret')
|
|
try:
|
|
content = request.content.read()
|
|
content_text = bytes2unicode(content)
|
|
payload = json.loads(content_text)
|
|
except Exception as exception:
|
|
raise ValueError('Error loading JSON: ' + str(exception))
|
|
|
|
if self.secret is not None:
|
|
p = Properties()
|
|
p.master = self.master
|
|
rendered_secret = yield p.render(self.secret)
|
|
signature = hmac.new(
|
|
unicode2bytes(rendered_secret),
|
|
unicode2bytes(content_text.strip()),
|
|
digestmod=hashlib.sha256)
|
|
header_signature = bytes2unicode(
|
|
request.getHeader(_HEADER_SIGNATURE))
|
|
self.verifyTransifexSignature(
|
|
request, content, rendered_secret,
|
|
signature, header_signature
|
|
)
|
|
|
|
event_type = bytes2unicode(payload.get(_EVENT_KEY), "None")
|
|
log.msg("Received event '{}' from transifex".format(event_type))
|
|
|
|
codebase = ""
|
|
changes = []
|
|
|
|
handler_function = getattr(self, 'process_{}'.format(event_type), None)
|
|
if not handler_function:
|
|
log.msg("Ignoring transifex event '{}'".format(event_type))
|
|
else:
|
|
changes = handler_function(payload, event_type, codebase)
|
|
|
|
return (changes, 'transifex')
|
|
|
|
|
|
transifex = TransifexHandler(transifex_dict)
|