def __init__(self, *args, **kwargs): super(WikipediaPlugin, self).__init__(*args, **kwargs) self.store = KeyValueStore(self.name) if not self.store.has('apiurl'): self.store.set( 'apiurl', 'https://%s.wikipedia.org/w/api.php' ) self.lang = 'en'
def __init__(self, *args, **kwargs): super(PrometheusPlugin, self).__init__(*args, **kwargs) self.store = KeyValueStore(self.name) self.rooms = RoomContextStore( [PrometheusPlugin.TYPE_TRACK] ) self.queue_counter = 1L self.consumer = MessageConsumer(self.matrix) self.consumer.daemon = True self.consumer.start()
def __init__(self, *args, **kwargs): super(JenkinsPlugin, self).__init__(*args, **kwargs) self.store = KeyValueStore(self.name) self.rooms = RoomContextStore([JenkinsPlugin.TYPE_TRACK]) if not self.store.has("known_projects"): self.store.set("known_projects", []) if not self.store.has("secret_token"): self.store.set("secret_token", "") self.failed_builds = { # projectName:branch: { commit:x } }
def __init__(self, *args, **kwargs): super(GithubPlugin, self).__init__(*args, **kwargs) self.store = KeyValueStore(self.name) self.rooms = RoomContextStore([GithubPlugin.TYPE_TRACK]) if not self.store.has("known_projects"): self.store.set("known_projects", []) if not self.store.has("secret_token"): self.store.set("secret_token", "") if not self.store.has("github_access_token"): log.info( "A github access_token is required to create github issues.") log.info("Issues will be created as this user.") token = raw_input("(Optional) Github token: ").strip() if token: self.store.set("github_access_token", token) else: log.info("You will not be able to create Github issues.")
def __init__(self, *args, **kwargs): super(GooglePlugin, self).__init__(*args, **kwargs) self.store = KeyValueStore(self.name) self.search_state = {} if not self.store.has('apiurl'): self.store.set('apiurl', 'https://www.googleapis.com/customsearch/v1') if not self.store.has('apikey'): print('API key is required to search with google customserchapi.') apikey = raw_input('Google Custom Search API key: ').strip() if apikey: self.store.set('apikey', apikey) if not self.store.has('cx'): print('Custom search engine ID required' 'to search with google customserchapi.') cx = raw_input( 'Google Custom Search id (https://cse.google.com): ').strip() if cx: self.store.set('cx', cx)
class PrometheusPlugin(Plugin): """Plugin for interacting with Prometheus.""" name = "prometheus" # Webhooks: # /neb/prometheus TYPE_TRACK = "org.matrix.neb.plugin.prometheus.projects.tracking" def __init__(self, *args, **kwargs): super(PrometheusPlugin, self).__init__(*args, **kwargs) self.store = KeyValueStore(self.name) self.rooms = RoomContextStore( [PrometheusPlugin.TYPE_TRACK] ) self.queue_counter = 1L self.consumer = MessageConsumer(self.matrix) self.consumer.daemon = True self.consumer.start() def on_event(self, event, event_type): self.rooms.update(event) def on_sync(self, sync): log.debug("Plugin: Prometheus sync state:") self.rooms.init_from_sync(sync) def get_webhook_key(self): return "prometheus" def on_receive_webhook(self, url, data, ip, headers): json_data = json.loads(data) log.info("recv %s", json_data) template = Template(self.store.get("message_template")) for alert in json_data.get("alert", []): for room_id in self.rooms.get_room_ids(): log.debug( "Queued message for room " + room_id + " at " + str(self.queue_counter) + ": %s", alert ) queue.put((self.queue_counter, room_id, template.render(alert))) self.queue_counter += 1
class GithubPlugin(Plugin): """Plugin for interacting with Github. github show projects : Show which github projects this bot recognises. github show track|tracking : Show which projects are being tracked. github track "owner/repo" "owner/repo" : Track the given projects. github add owner/repo : Add the given repo to the tracking list. github remove owner/repo : Remove the given repo from the tracking list. github stop track|tracking : Stop tracking github projects. github create owner/repo "Bug title" "Bug desc" : Create an issue on Github. github label add|remove owner/repo issue# label : Label an issue on Github. """ name = "github" # New events: # Type: org.matrix.neb.plugin.github.projects.tracking # State: Yes # Content: { # projects: [projectName1, projectName2, ...] # } # Webhooks: # /neb/github TYPE_TRACK = "org.matrix.neb.plugin.github.projects.tracking" TYPE_COLOR = "org.matrix.neb.plugin.github.projects.color" TRACKING = ["track", "tracking"] def __init__(self, *args, **kwargs): super(GithubPlugin, self).__init__(*args, **kwargs) self.store = KeyValueStore(self.name) self.rooms = RoomContextStore([GithubPlugin.TYPE_TRACK]) if not self.store.has("known_projects"): self.store.set("known_projects", []) if not self.store.has("secret_token"): self.store.set("secret_token", "") if not self.store.has("github_access_token"): log.info( "A github access_token is required to create github issues.") log.info("Issues will be created as this user.") token = raw_input("(Optional) Github token: ").strip() if token: self.store.set("github_access_token", token) else: log.info("You will not be able to create Github issues.") def on_receive_github_push(self, info): log.info("recv %s", info) # add the project if we didn't know about it before if info["repo"] not in self.store.get("known_projects"): log.info("Added new repo: %s", info["repo"]) projects = self.store.get("known_projects") projects.append(info["repo"]) self.store.set("known_projects", projects) push_message = "" if info["type"] == "delete": push_message = '[<u>%s</u>] %s <font color="red"><b>deleted</font> %s</b>' % ( info["repo"], info["commit_username"], info["branch"]) elif info["type"] == "commit": # form the template: # [<repo>] <username> pushed <num> commits to <branch>: <git.io link> # 1<=3 of <branch name> <short hash> <full username>: <comment> if info["num_commits"] == 1: push_message = "[<u>%s</u>] %s pushed to <b>%s</b>: %s - %s" % ( info["repo"], info["commit_username"], info["branch"], info["commit_msg"], info["commit_link"]) else: summary = "" max_commits = 3 count = 0 for c in info["commits_summary"]: if count == max_commits: break summary += "\n%s: %s" % (c["author"], c["summary"]) count += 1 push_message = "[<u>%s</u>] %s pushed %s commits to <b>%s</b>: %s %s" % ( info["repo"], info["commit_username"], info["num_commits"], info["branch"], info["commit_link"], summary) else: log.warn("Unknown push type. %s", info["type"]) return self.send_message_to_repos(info["repo"], push_message) def send_message_to_repos(self, repo, push_message): # send messages to all rooms registered with this project. for room_id in self.rooms.get_room_ids(): try: if repo in self.rooms.get_content( room_id, GithubPlugin.TYPE_TRACK)["projects"]: self.matrix.send_message_event( room_id, "m.room.message", self.matrix.get_html_body(push_message, msgtype="m.notice")) except KeyError: pass def cmd_show(self, event, action): """Show information on projects or projects being tracked. Show which projects are being tracked. 'github show tracking' Show which proejcts are recognised so they could be tracked. 'github show projects' """ if action == "projects": projects = self.store.get("known_projects") return "Available projects: %s - To add more projects, you must register a webhook on Github." % json.dumps( projects) elif action in self.TRACKING: return self._get_tracking(event["room_id"]) else: return self.cmd_show.__doc__ @admin_only def cmd_add(self, event, repo): """Add a repo for tracking. 'github add owner/repo'""" if repo not in self.store.get("known_projects"): return "Unknown project name: %s." % repo try: room_repos = self.rooms.get_content( event["room_id"], GithubPlugin.TYPE_TRACK)["projects"] except KeyError: room_repos = [] if repo in room_repos: return "%s is already being tracked." % repo room_repos.append(repo) self._send_track_event(event["room_id"], room_repos) return "Added %s. Commits for projects %s will be displayed as they are commited." % ( repo, room_repos) @admin_only def cmd_remove(self, event, repo): """Remove a repo from tracking. 'github remove owner/repo'""" try: room_repos = self.rooms.get_content( event["room_id"], GithubPlugin.TYPE_TRACK)["projects"] except KeyError: room_repos = [] if repo not in room_repos: return "Cannot remove %s : It isn't being tracked." % repo room_repos.remove(repo) self._send_track_event(event["room_id"], room_repos) return "Removed %s. Commits for projects %s will be displayed as they are commited." % ( repo, room_repos) @admin_only def cmd_track(self, event, *args): if len(args) == 0: return self._get_tracking(event["room_id"]) for project in args: if project not in self.store.get("known_projects"): return "Unknown project name: %s." % project self._send_track_event(event["room_id"], args) return "Commits for projects %s will be displayed as they are commited." % ( args, ) @admin_only def cmd_stop(self, event, action): """Stop tracking projects. 'github stop tracking'""" if action in self.TRACKING: self._send_track_event(event["room_id"], []) return "Stopped tracking projects." else: return self.cmd_stop.__doc__ @admin_only def cmd_create(self, event, *args): """Create a new issue. Format: 'create <owner/repo> <title> <desc(optional)>' E.g. 'create matrix-org/synapse A bug goes here 'create matrix-org/synapse "Title here" "desc here" """ if not args or len(args) < 2: return self.cmd_create.__doc__ project = args[0] others = args[1:] # others must contain a title, may contain a description. If it contains # a description, it MUST be in [1] and be longer than 1 word. title = ' '.join(others) desc = "" try: possible_desc = others[1] if ' ' in possible_desc: desc = possible_desc title = others[0] except: pass return self._create_issue(event["user_id"], project, title, desc) @admin_only def cmd_label_remove(self, event, repo, issue_num, *args): """ Remove a label on an issue. Format: 'label remove <owner/repo> <issue num> <label> <label> <label>' E.g. 'label remove matrix-org/synapse 323 bug p2 blocked' """ e = self._is_valid_issue_request(repo, issue_num) if e: return e if len(args) == 0: return "You must specify at least one label." errs = [] for label in args: url = "https://api.github.com/repos/%s/issues/%s/labels/%s" % ( repo, issue_num, label) res = requests.delete(url, headers={ "Authorization": "token %s" % self.store.get("github_access_token") }) if res.status_code < 200 or res.status_code >= 300: errs.append("Problem removing label %s : HTTP %s" % (label, res.status_code)) return errs if len(errs) == 0: return "Removed labels %s" % (json.dumps(args), ) else: return "There was a problem removing some labels:\n" + "\n".join( errs) @admin_only def cmd_label_add(self, event, repo, issue_num, *args): """Label an issue. Format: 'label add <owner/repo> <issue num> <label> <label> <label>' E.g. 'label add matrix-org/synapse 323 bug p2 blocked' """ e = self._is_valid_issue_request(repo, issue_num) if e: return e if len(args) == 0: return "You must specify at least one label." url = "https://api.github.com/repos/%s/issues/%s/labels" % (repo, issue_num) res = requests.post(url, data=json.dumps(args), headers={ "Content-Type": "application/json", "Authorization": "token %s" % self.store.get("github_access_token") }) if res.status_code < 200 or res.status_code >= 300: err = "%s Failed: HTTP %s" % ( url, res.status_code, ) log.error(err) return err return "Added labels %s" % (json.dumps(args), ) def _create_issue(self, user_id, project, title, desc=""): if not self.store.has("github_access_token"): return "This plugin isn't configured to create Github issues." # Add a space after the @ to avoid pinging people on Github! user_id = user_id.replace("@", "@ ") desc = "Created by %s.\n\n%s" % (user_id, desc) info = {"title": title, "body": desc} url = "https://api.github.com/repos/%s/issues" % project res = requests.post(url, data=json.dumps(info), headers={ "Content-Type": "application/json", "Authorization": "token %s" % self.store.get("github_access_token") }) if res.status_code < 200 or res.status_code >= 300: err = "%s Failed: HTTP %s" % ( url, res.status_code, ) log.error(err) return err response = json.loads(res.text) return "Created issue: %s" % response["html_url"] def _is_valid_issue_request(self, repo, issue_num): issue_is_num = True try: issue_is_num = int(issue_num) except ValueError: issue_is_num = False if "/" not in repo: return "Repo must be in the form 'owner/repo' e.g. 'matrix-org/synapse'." if not issue_is_num: return "Issue number must be a number" if not self.store.has("github_access_token"): return "This plugin isn't configured to interact with Github issues." def _send_track_event(self, room_id, project_names): self.matrix.send_state_event(room_id, self.TYPE_TRACK, {"projects": project_names}) def _get_tracking(self, room_id): try: return ("Currently tracking %s" % json.dumps( self.rooms.get_content(room_id, GithubPlugin.TYPE_TRACK)["projects"])) except KeyError: return "Not tracking any projects currently." def on_event(self, event, event_type): self.rooms.update(event) def on_sync(self, sync): log.debug("Plugin: Github sync state:") self.rooms.init_from_sync(sync) def get_webhook_key(self): return "github" def on_receive_pull_request(self, data): action = data["action"] pull_req_num = data["number"] repo_name = data["repository"]["full_name"] pr = data["pull_request"] pr_url = pr["html_url"] pr_state = pr["state"] pr_title = pr["title"] user = data["sender"]["login"] action_target = "" if pr.get("assignee") and pr["assignee"].get("login"): action_target = " to %s" % (pr["assignee"]["login"], ) msg = "[<u>%s</u>] %s %s <b>pull request #%s</b>: %s [%s]%s - %s" % ( repo_name, user, action, pull_req_num, pr_title, pr_state, action_target, pr_url) self.send_message_to_repos(repo_name, msg) def on_receive_create(self, data): if data["ref_type"] != "branch": return # only echo branch creations for now. branch_name = data["ref"] user = data["sender"]["login"] repo_name = data["repository"]["full_name"] msg = '[<u>%s</u>] %s <font color="green">created</font> a new branch: <b>%s</b>' % ( repo_name, user, branch_name) self.send_message_to_repos(repo_name, msg) def on_receive_ping(self, data): repo_name = data.get("repository", {}).get("full_name") # add the project if we didn't know about it before if repo_name and repo_name not in self.store.get("known_projects"): log.info("Added new repo: %s", repo_name) projects = self.store.get("known_projects") projects.append(repo_name) self.store.set("known_projects", projects) def on_receive_comment(self, data): repo_name = data["repository"]["full_name"] issue = data["issue"] comment = data["comment"] is_pull_request = "pull_request" in issue if not is_pull_request: return # don't bother displaying issue comments pr_title = issue["title"] pr_num = issue["number"] pr_username = issue["user"]["login"] comment_url = comment["html_url"] username = comment["user"]["login"] msg = "[<u>%s</u>] %s commented on %s's <b>pull request #%s</b>: %s - %s" % ( repo_name, username, pr_username, pr_num, pr_title, comment_url) self.send_message_to_repos(repo_name, msg) def on_receive_pull_request_comment(self, data): repo_name = data["repository"]["full_name"] username = data["sender"]["login"] pull_request = data["pull_request"] pr_username = pull_request["user"]["login"] pr_num = pull_request["number"] assignee = "None" if data["pull_request"].get("assignee"): assignee = data["pull_request"]["assignee"]["login"] pr_title = pull_request["title"] comment_url = data["comment"]["html_url"] msg = "[<u>%s</u>] %s made a line comment on %s's <b>pull request #%s</b> (assignee: %s): %s - %s" % ( repo_name, username, pr_username, pr_num, assignee, pr_title, comment_url) self.send_message_to_repos(repo_name, msg) def on_receive_issue(self, data): action = data["action"] repo_name = data["repository"]["full_name"] issue = data["issue"] title = issue["title"] issue_num = issue["number"] url = issue["html_url"] user = data["sender"]["login"] if action == "assigned": try: assignee = data["assignee"]["login"] msg = "[<u>%s</u>] %s assigned issue #%s to %s: %s - %s" % ( repo_name, user, issue_num, assignee, title, url) self.send_message_to_repos(repo_name, msg) return except: pass msg = "[<u>%s</u>] %s %s issue #%s: %s - %s" % ( repo_name, user, action, issue_num, title, url) self.send_message_to_repos(repo_name, msg) def on_receive_webhook(self, url, data, ip, headers): if self.store.get("secret_token"): token_sha1 = headers.get('X-Hub-Signature') payload_body = data calc = hmac.new(str(self.store.get("secret_token")), payload_body, sha1) calc_sha1 = "sha1=" + calc.hexdigest() if token_sha1 != calc_sha1: log.warn("GithubWebServer: FAILED SECRET TOKEN AUTH. IP=%s", ip) return ("", 403, {}) json_data = json.loads(data) is_private_repo = json_data.get("repository", {}).get("private") if is_private_repo: log.info("Received private repo event for %s", json_data["repository"].get("name")) return event_type = headers.get('X-GitHub-Event') if event_type == "pull_request": self.on_receive_pull_request(json_data) return elif event_type == "issues": self.on_receive_issue(json_data) return elif event_type == "create": self.on_receive_create(json_data) return elif event_type == "ping": self.on_receive_ping(json_data) return elif event_type == "issue_comment": # INCLUDES PR COMMENTS!!! # But not line comments! self.on_receive_comment(json_data) return elif event_type == "pull_request_review_comment": self.on_receive_pull_request_comment(json_data) return j = json_data repo_name = j["repository"]["full_name"] # strip 'refs/heads' from 'refs/heads/branch_name' branch = '/'.join(j["ref"].split('/')[2:]) commit_msg = "" commit_name = "" commit_link = "" short_hash = "" push_type = "commit" if j["head_commit"]: commit_msg = j["head_commit"]["message"] commit_name = j["head_commit"]["committer"]["name"] commit_link = j["head_commit"]["url"] # short hash please short_hash = commit_link.split('/')[-1][0:8] commit_link = '/'.join( commit_link.split('/')[0:-1]) + "/" + short_hash elif j["deleted"]: # looks like this branch was deleted, no commit and deleted=true commit_name = j["pusher"]["name"] push_type = "delete" commit_uname = None try: commit_uname = j["head_commit"]["committer"]["username"] except Exception: # possible if they haven't tied up with a github account commit_uname = commit_name # look for multiple commits num_commits = 1 commits_summary = [] if "commits" in j and len(j["commits"]) > 1: num_commits = len(j["commits"]) for c in j["commits"]: cname = None try: cname = c["author"]["username"] except: cname = c["author"]["name"] commits_summary.append({ "author": cname, "summary": c["message"] }) self.on_receive_github_push({ "branch": branch, "repo": repo_name, "commit_msg": commit_msg, "commit_username": commit_uname, "commit_name": commit_name, "commit_link": commit_link, "commit_hash": short_hash, "type": push_type, "num_commits": num_commits, "commits_summary": commits_summary })
class GooglePlugin(Plugin): """Google search. (Default) google search <text to search> google image <text to search> google next""" name = 'google' default_method = 'cmd_search' def __init__(self, *args, **kwargs): super(GooglePlugin, self).__init__(*args, **kwargs) self.store = KeyValueStore(self.name) self.search_state = {} if not self.store.has('apiurl'): self.store.set('apiurl', 'https://www.googleapis.com/customsearch/v1') if not self.store.has('apikey'): print('API key is required to search with google customserchapi.') apikey = raw_input('Google Custom Search API key: ').strip() if apikey: self.store.set('apikey', apikey) if not self.store.has('cx'): print('Custom search engine ID required' 'to search with google customserchapi.') cx = raw_input( 'Google Custom Search id (https://cse.google.com): ').strip() if cx: self.store.set('cx', cx) def cmd_search(self, event, *args): """Google search (default method). 'google search <text>'""" query = {} query['q'] = ' '.join(args) query['num'] = 1 query['start'] = 1 query['alt'] = 'json' query['key'] = self.store.get('apikey') query['cx'] = self.store.get('cx') res = self._gquery(query, event) if 'items' in res: self.matrix.send_message(event['room_id'], res['items'][0]['link']) return None else: return self.tr.trans('Nothing found...') def cmd_image(self, event, *args): """Google search image. 'google image <text>'""" query = {} query['q'] = ' '.join(args) query['num'] = 1 query['start'] = 1 query['searchType'] = 'image' query['imgSize'] = 'large' query['alt'] = 'json' query['key'] = self.store.get('apikey') query['cx'] = self.store.get('cx') res = self._gquery(query, event) if 'items' in res: if self._send_image(res, event): return None else: return self.tr.trans('Image upload error.') else: return self.tr.trans('Nothing found...') def cmd_next(self, event, *args): """Search next. 'google next'""" if event['room_id'] in self.search_state: query = self.search_state[event['room_id']] query['start'] = query['start'] + 1 res = self._gquery(query, event) if 'items' in res: if 'searchType' in query: if query['searchType'] == 'image': # image query if self._send_image(res, event): return None else: return self.tr.trans('Image upload error.') else: log.error('Strange query %r in room %s' % (query, event['room_id'])) else: # simple query self.matrix.send_message(event['room_id'], res['items'][0]['link']) return None else: return self.tr.trans('Nothing found...') else: return self.tr.trans('No pervous search in this room.') cmd_more = cmd_next """Search next. 'google more'""" def _send_image(self, res, event): """Send image to room""" # TODO add more error handling try: i = requests.get(res['items'][0]['link'], timeout=30) except ConnectionError as err: log.error('Image %s download error: %r' % (res['items'][0]['link'], err)) return False ires = self.matrix.media_upload(i.content, res['items'][0]['mime']) if 'content_uri' in ires: self.matrix.send_content(event['room_id'], ires['content_uri'], res['items'][0]['title'], 'm.image') return True else: log.error('Image upload error: %r' % ires) return False def _gquery(self, query, event): """Google query func.""" self.search_state[event['room_id']] = query r = requests.get(self.store.get('apiurl'), params=query) if 'error' in r.json(): log.error('Google search error: %r' % r.json()['error']) log.debug('Google query result: %r' % r.json()) return r.json()
class JenkinsPlugin(Plugin): """ Plugin for receiving Jenkins notifications via the Notification Plugin. jenkins show projects : Display which projects this bot recognises. jenkins show track|tracking : Display which projects this bot is tracking. jenkins track project1 project2 ... : Track Jenkins notifications for the named projects. jenkins stop track|tracking : Stop tracking Jenkins notifications. jenkins add projectName : Start tracking projectName. jenkins remove projectName : Stop tracking projectName. """ name = "jenkins" # https://wiki.jenkins-ci.org/display/JENKINS/Notification+Plugin # New events: # Type: org.matrix.neb.plugin.jenkins.projects.tracking # State: Yes # Content: { # projects: [projectName1, projectName2, ...] # } # Webhooks: # /neb/jenkins TRACKING = ["track", "tracking"] TYPE_TRACK = "org.matrix.neb.plugin.jenkins.projects.tracking" def __init__(self, *args, **kwargs): super(JenkinsPlugin, self).__init__(*args, **kwargs) self.store = KeyValueStore(self.name) self.rooms = RoomContextStore([JenkinsPlugin.TYPE_TRACK]) if not self.store.has("known_projects"): self.store.set("known_projects", []) if not self.store.has("secret_token"): self.store.set("secret_token", "") self.failed_builds = { # projectName:branch: { commit:x } } def cmd_show(self, event, action): """Show information on projects or projects being tracked. Show which projects are being tracked. 'jenkins show tracking' Show which proejcts are recognised so they could be tracked. 'jenkins show projects' """ if action in self.TRACKING: return self._get_tracking(event["room_id"]) elif action == "projects": projects = self.store.get("known_projects") return "Available projects: %s" % json.dumps(projects) else: return "Invalid arg '%s'.\n %s" % (action, self.cmd_show.__doc__) @admin_only def cmd_track(self, event, *args): """Track projects. 'jenkins track Foo "bar with spaces"'""" if len(args) == 0: return self._get_tracking(event["room_id"]) for project in args: if project not in self.store.get("known_projects"): return "Unknown project name: %s." % project self._send_track_event(event["room_id"], args) return "Jenkins notifications for projects %s will be displayed when they fail." % ( args) @admin_only def cmd_add(self, event, project): """Add a project for tracking. 'jenkins add projectName'""" if project not in self.store.get("known_projects"): return "Unknown project name: %s." % project try: room_projects = self.rooms.get_content( event["room_id"], JenkinsPlugin.TYPE_TRACK)["projects"] except KeyError: room_projects = [] if project in room_projects: return "%s is already being tracked." % project room_projects.append(project) self._send_track_event(event["room_id"], room_projects) return "Added %s. Jenkins notifications for projects %s will be displayed when they fail." % ( project, room_projects) @admin_only def cmd_remove(self, event, project): """Remove a project from tracking. 'jenkins remove projectName'""" try: room_projects = self.rooms.get_content( event["room_id"], JenkinsPlugin.TYPE_TRACK)["projects"] except KeyError: room_projects = [] if project not in room_projects: return "Cannot remove %s : It isn't being tracked." % project room_projects.remove(project) self._send_track_event(event["room_id"], room_projects) return "Removed %s. Jenkins notifications for projects %s will be displayed when they fail." % ( project, room_projects) @admin_only def cmd_stop(self, event, action): """Stop tracking projects. 'jenkins stop tracking'""" if action in self.TRACKING: self._send_track_event(event["room_id"], []) return "Stopped tracking projects." else: return "Invalid arg '%s'.\n %s" % (action, self.cmd_stop.__doc__) def _get_tracking(self, room_id): try: return ("Currently tracking %s" % json.dumps( self.rooms.get_content(room_id, JenkinsPlugin.TYPE_TRACK)["projects"])) except KeyError: return "Not tracking any projects currently." def _send_track_event(self, room_id, project_names): self.matrix.send_state_event(room_id, self.TYPE_TRACK, {"projects": project_names}) def send_message_to_repos(self, repo, push_message): # send messages to all rooms registered with this project. for room_id in self.rooms.get_room_ids(): try: if (repo in self.rooms.get_content( room_id, JenkinsPlugin.TYPE_TRACK)["projects"]): self.matrix.send_message_event( room_id, "m.room.message", self.matrix.get_html_body(push_message, msgtype="m.notice")) except KeyError: pass def on_event(self, event, event_type): self.rooms.update(event) def on_sync(self, sync): log.debug("Plugin: Jenkins sync state:") self.rooms.init_from_sync(sync) def get_webhook_key(self): return "jenkins" def on_receive_webhook(self, url, data, ip, headers): # data is of the form: # { # "name":"Synapse", # "url":"job/Synapse/", # "build": { # "full_url":"http://*****:*****@github.com:matrix-org/synapse.git", # "branch":"origin/develop", # "commit":"72aef114ab1201f5a5cd734220c9ec738c4e2910" # }, # "artifacts":{} # } # } log.info("URL: %s", url) log.info("Data: %s", data) log.info("Headers: %s", headers) j = json.loads(data) name = j["name"] query_dict = urlparse.parse_qs(urlparse.urlparse(url).query) if self.store.get("secret_token"): if "secret" not in query_dict: log.warn("Jenkins webhook: Missing secret.") return ("", 403, {}) # The jenkins Notification plugin does not support any sort of # "execute this code on this json object before you send" so we can't # send across HMAC SHA1s like with github :( so a secret token will # have to do. secrets = query_dict["secret"] if len(secrets) > 1: log.warn( "Jenkins webhook: FAILED SECRET TOKEN AUTH. Too many secrets. IP=%s", ip) return ("", 403, {}) elif secrets[0] != self.store.get("secret_token"): log.warn( "Jenkins webhook: FAILED SECRET TOKEN AUTH. Mismatch. IP=%s", ip) return ("", 403, {}) else: log.info("Jenkins webhook: Secret verified.") # add the project if we didn't know about it before if name not in self.store.get("known_projects"): log.info("Added new job: %s", name) projects = self.store.get("known_projects") projects.append(name) self.store.set("known_projects", projects) status = j["build"]["status"] branch = None commit = None git_url = None jenkins_url = None info = "" try: branch = j["build"]["scm"]["branch"] commit = j["build"]["scm"]["commit"] git_url = j["build"]["scm"]["url"] jenkins_url = j["build"]["full_url"] # try to format the git url nicely if (git_url.startswith("*****@*****.**") and git_url.endswith(".git")): # [email protected]:matrix-org/synapse.git org_and_repo = git_url.split(":")[1][:-4] commit = "https://github.com/%s/commit/%s" % (org_and_repo, commit) info = "%s commit %s - %s" % (branch, commit, jenkins_url) except KeyError: pass fail_key = "%s:%s" % (name, branch) if status.upper() != "SUCCESS": # complain msg = '<font color="red">[%s] <b>%s - %s</b></font>' % ( name, status, info) if fail_key in self.failed_builds: info = "%s failing since commit %s - %s" % ( branch, self.failed_builds[fail_key]["commit"], jenkins_url) msg = '<font color="red">[%s] <b>%s - %s</b></font>' % ( name, status, info) else: # add it to the list self.failed_builds[fail_key] = {"commit": commit} self.send_message_to_repos(name, msg) else: # do we need to prod people? if fail_key in self.failed_builds: info = "%s commit %s" % (branch, commit) msg = '<font color="green">[%s] <b>%s - %s</b></font>' % ( name, status, info) self.send_message_to_repos(name, msg) self.failed_builds.pop(fail_key)
class WikipediaPlugin(Plugin): """ Wikipedia search plugin. Usage: 'wiki <text to search>' Example: 'wiki russian desman' """ name = 'wiki' default_method = 'cmd_search' def __init__(self, *args, **kwargs): super(WikipediaPlugin, self).__init__(*args, **kwargs) self.store = KeyValueStore(self.name) if not self.store.has('apiurl'): self.store.set( 'apiurl', 'https://%s.wikipedia.org/w/api.php' ) self.lang = 'en' def _qwiki(self, query): """ Query wikipedia API """ r = requests.get(self.store.get('apiurl') % self.lang, params=query) if 'error' in r.json(): log.error('Wikipedia search: %r' % r.json()['error']) # return self.tr.trans('When searching on Wikipedia, an error occurred.') else: if 'warnings' in r.json(): log.warn('Wikipedia search: %r' % r.json()['warnings']) if 'query' in r.json(): if 'normalized' in r.json()['query']: query['titles'] = r.json()['query']['normalized'][0]['to'] log.debug("Wikipedia query to %s is normalized to %s" % ( r.json()['query']['normalized'][0]['from'], r.json()['query']['normalized'][0]['to'] )) return self._qwiki(query) if 'pages' in r.json()['query']: return r.json()['query']['pages'] else: log.debug('Wikipedia result without pages: %r %r' % (query, r.json())) # return self.tr.trans('Nothing found.') def _get_url(self, pageid): """ Get wiki URL by pageid """ query = {} query['action'] = 'query' query['prop'] = 'info' query['format'] = 'json' query['inprop'] = 'url' query['pageids'] = pageid r = requests.get(self.store.get('apiurl') % self.lang, params=query) if 'error' in r.json(): log.error('Wikipedia search: %r' % r.json()['error']) else: if 'warnings' in r.json(): log.warn('Wikipedia search: %r' % r.json()['warnings']) if 'query' in r.json(): if 'pages' in r.json()['query']: for page in r.json()['query']['pages']: if 'fullurl' in r.json()['query']['pages'][page]: return r.json()['query']['pages'][page]['fullurl'] return None @civility def cmd_search(self, event, *args): """Search in wikipedia. 'wiki <text to search>'""" self.lang = self.tr.detect_lang(' '.join(args)) query = {} query['action'] = 'query' query['prop'] = 'extracts' query['format'] = 'json' query['redirects'] = 1 query['titles'] = ' '.join(args) res = [] pages = self._qwiki(query) for page in pages: if 'extract' in pages[page]: self.send_html( event['room_id'], pages[page]['extract'] ) link = self._get_url(page) if link: res.append(link) if 'missing' in pages[page]: log.debug('Wikipedia result missing page: %r' % pages[page]) return self.tr.trans('Page %s is missing') % ' '.join(args) # After text return collected links return res