class SetReviewerWhenMovedToRFR(ConditionalTask): """This task adding a reviewer once the title includes an '[RFR]' tag""" Endpoint = Github() # The third party Endpoint for this task is Github. EndpointScope = PullRequest # The scope of this task is pull request. NAME = 'SetReviewerWhenMovedToRFR' # The name of the task. @property def condition(self): # We get the `title_tags` and `reviewers` statistics to check whether the title tag has moved # to RFR and that there are no reviewers that have been assigned. return ('RFR' in self.statistics.my_pulls_statistics.title_tags and not self.statistics.my_pulls_statistics.reviewers) def get_data(self): """Collecting data""" repos_data = next( repo for repo in CurrentProject().config.config.github.repositories if repo.name == self.statistics.my_repo_statistics.repository) maintainers = repos_data.maintainers reviewer = choice(maintainers) owner = self.statistics.my_pulls_statistics.owner reviewer_contact = User.get_user(github_login=reviewer) owner_contact = User.get_user(github_login=owner) or owner return owner_contact, reviewer_contact def get_artifacts(self): return self.statistics.my_pulls_statistics.title_tags def run(self): """Running the task""" owner_contact, reviewer_contact = self.get_data() owner_contact = (owner_contact.irc_nick if isinstance( owner_contact, User) else owner_contact) self.scopes[PullRequest].add_reviewers(reviewer_contact.github)
class GithubEventsFactory(EventsFactory): Endpoint = Github() _max_recent_check = 100 # Checking for at most <_max_recent_check> recent events and then break TODO: parameterize def build_events(self) -> dict: events = [] event_getter_names = ('get_events', 'get_issues_events') for repo in self.Endpoint.repositories: for getter in event_getter_names: i = 0 for event in getattr(repo, getter)(): # We assume that the most recent event are in the top of the timeline in github, so if the first we receive # is already in the buffer or delivered, we assume that there are no new events. hsh = GithubEventBase.hash_by_id(event.id) if hsh in map(lambda e: e.hash, self._events_buffer): self.logger.debug('Event "{}" already in the buffer. dismissing...'.format(hsh)) break if hsh in self._dilivered_events_stack: self.logger.debug('Event "{}" already delivered. dismissing...'.format(hsh)) break if i >= self._max_recent_check: self.logger.debug('Max recent checks exceeded for getter "{}", events collected: {}...'.format( getter, len(events), hsh)) break # TODO: Check whether the following is necessary... # elif event.actor.login == CurrentProject().config.credentials.github.username: # continue try: data = event.raw_data except UnknownObjectException: break # stale event # Fill some required fields that could be missing in the timeline API but data['organization'], data['repository'] = getattr(repo.owner, 'login', repo.owner.name), repo.name data['sender'] = {'login': event.actor.login} data['type'] = data.get('type') or data.get('event') # Gathering facts payload = data.get('payload') or {} issue = payload.get('issue') or data.get('issue') pull_request = (issue or {}).get('pull_request') or payload.get('pull_request') # Classifying event: if pull_request: data['issue_number'] = int(pull_request['url'].split('/')[-1]) event_obj = PullRequestEvent(data, event.artifacts) elif issue: data['issue_number'] = issue['number'] event_obj = IssueEvent(data, event.artifacts) else: event_obj = RepositoryEvent(data, event.artifacts) # events.append(event_obj) # since we have number of getters we want to collect only (1 / len(event_getter_names)) # from each getter so we are adding the following to `i` i += 1.0 * len(event_getter_names) return events
class GithubScopesCollector(ScopesCollector): Endpoint = Github() def collect_all(self): scopes = [] for repo in self.Endpoint.repositories: scopes.append(repo) for pull_request in repo.get_pulls(): pull_request.repository = repo scopes.append(pull_request) for issue in repo.get_issues(): if not issue.pull_request: issue.repository = repo scopes.append(issue) return scopes
class AlertOnMergedEvent(ConditionalTask): """Alert when some pull request has been merged""" Endpoint = Github() # The third party Endpoint for this task is Github. EndpointScope = Repository # The scope of this task is pull request. NAME = 'AlertOnMergedEvent' # The name of the task. @property def condition(self): return (self.event and self.event.data['type'] == 'PullRequestEvent' and getnode(self.event.data, ['payload', 'action']) == 'closed' and getnode(self.event.data, ['payload', 'pull_request', 'merged'])) def get_artifacts(self): return [str(self.event.data['id'])] def run(self): actor = self.event.data['sender']['login'] number = self.event.data['payload']['pull_request']['number'] IRCendpoint().client.msg('##bot-testing', f'{actor} has merged PR#{number}')
class PromptWhenLargeNumberOfComments(ConditionalTask): """This task is prompting on IRC when there is a large number of comment in a pull request""" Endpoint = Github() # The third party Endpoint for this task is Github. EndpointScope = PullRequest # The scope of this task is pull request. NAME = 'PromptWhenLargeNumberOfComments' # The name of the task. PR_MAX_NUMBER_OF_COMMENTS = 10 @property def condition(self): # Checking that total number of comment is greater than 10. return self.statistics.my_pr_stats.total_number_of_comments > self.PR_MAX_NUMBER_OF_COMMENTS def get_artifacts(self): return [str(self.statistics.my_pr_stats.total_number_of_comments)] def run(self): """Running the task""" IRCendpoint().client.msg( '##bot-testing', f'PR#{self.statistics.my_pr_stats.number} has more than {self.PR_MAX_NUMBER_OF_COMMENTS} comments! ' f'({self.statistics.my_pr_stats.total_number_of_comments} comments)' )
class AlertOnMentionedUser(ConditionalTask): """This task prompt the user in IRC once he was mentioned in some pull request.""" Endpoint = Github() EndpointScope = PullRequest NAME = 'AlertOnMentionedUser' RUN_ONCE = False def get_artifacts(self): return [str(self.event.data['id'])] @property def condition(self): """ Checking that the task triggered by event, the event is a comment event and there are mentioned users in the comment. """ return bool(self.event and self.event.artifacts and self.event.artifacts.get('comment') and self.event.artifacts['comment'].mentioned_users) def run(self): mentioned, actor = self.event.artifacts[ 'comment'].mentioned_users, self.event.artifacts['actor'] # Getting IRC nick in case that the actor in the users list user = User.get_user(github_login=actor.login) actor = (user.irc_nick if user else actor.login) for mentioned_user in mentioned: # Getting IRC nick in case that the user in the users list user = User.get_user(github_login=mentioned_user.login) mentioned_user = (user.irc_nick if user else mentioned_user.login) # Composing the comment from the statistics, mentioned users and actor IRCendpoint().client.msg( '##bot-testing', f'{mentioned_user}, {actor} has ' f'mentioned you in {self.statistics.my_repo_statistics.organization}/' f'{self.statistics.my_repo_statistics.repository} ' f'@ PR#{self.statistics.my_pulls_statistics.issue_number}.')
class GithubEventBase(Event): """A base class for Github event.""" Endpoint = Github() def __init__(self, data: dict, artifacts: dict): assert isinstance(data, dict) self._data = data self._artifacts = artifacts @property def artifacts(self) -> dict: return self._artifacts @property def type(self): return self.data['type'] @property def id(self): return self._data['id'] @property def data(self): return self._data
class GithubBot(BotSlave): Endpoint = Github() EventsFactory = GithubEventsFactory() ScopeCollector = GithubScopesCollector()
class GithubStatisticsBase(Statistics): Endpoint = Github()