def _calculate_rain(self): """Rain is calculated differently when active and inactive. We have constant propability to change between states, and we also include previous value and clouds as a factor. Note, that there must be at least some clouds for rain to fall. """ probability_change_state = 0.05 # extracted booleans: change_state = random.uniform(0, 1) < probability_change_state is_raining = self.weather['rain'] > 0 if self.weather['clouds'] > 0: if is_raining: if change_state: self.weather['rain'] = 0 else: old_factor = 0.3 new_factor = 1 - old_factor self.weather['rain'] = truncate( self.weather['rain'] * old_factor + self.weather['clouds'] * new_factor) else: if change_state and self.weather['clouds'] > 0.4: self.weather['rain'] = truncate(self.weather['clouds'] * 0.2) else: self.weather['rain'] = 0
def _calculate_clouds(self): """Clouds are calculated differently when active and inactive. We have different propabilities to change between states, and we also include previous value and wind as a factor. Altough, there is still a small chance of sudden storm or cleariness. Clouds have some critical value, and they tend to not pass through it. This allows us to simulate mainly moderate weather. """ probability_stop = 0.1 probability_start = 0.02 probability_clear_all = 0.004 probability_storm = 0.002 probability_pass_critical = 0.9 # extracted booleans: is_cloudy = self.weather['clouds'] > 0 going_to_stop = random.uniform(0, 1) < probability_stop going_to_start = random.uniform(0, 1) < probability_start suddenly_clear_all = random.uniform(0, 1) < probability_clear_all suddenly_storm = random.uniform(0, 1) < probability_storm passing_critical = random.uniform(0, 1) < probability_pass_critical if is_cloudy: if suddenly_clear_all: self.weather['clouds'] = 0 elif suddenly_storm: self.weather['clouds'] = random.uniform(0.8, 1) elif going_to_stop: self.weather['clouds'] = truncate(self.weather['clouds'] - random.uniform(0.05, 0.2)) else: old_factor = 0.95 wind_factor = 0.2 new_factor = 1 - old_factor new_cloud = random.betavariate(5, 1) # add new cloud clouds = (self.weather['clouds'] * old_factor + new_cloud * new_factor) # consider influence of wind clouds -= self.weather['wind'] * wind_factor # go go over 0.4 with the probability of passing critical value upper_limit = 0.4 if passing_critical: upper_limit = 1 # update clouds self.weather['clouds'] = truncate(clouds, 0, upper_limit) elif going_to_start: # start small, grow tall self.weather['clouds'] = random.uniform(0.05, 0.2)
def _calculate_temperature(self): """Calculated based on all previous factors, base and tendency. Base is the additional bias, very slowly changing, simulating long-term weather conditions, such as atmospheric fronts. Tendency is a direction for the base - it can be increasing (+1) or decreasing (-1). Tendency has a slight chance to change at any point in time. Besides, temperature is computed including its previous value and some hard-coded weights for different factors. """ # make changes in base temperature according to tendency change_tendency_probability = 0.001 base_change_probability = 0.05 tendency_changing = random.uniform(0, 1) < change_tendency_probability base_changing = random.uniform(0, 1) < base_change_probability if tendency_changing: self.tendency = -self.tendency if base_changing: old_factor = 0.9 new_factor = 1 - old_factor new_base_temperature = random.randrange(5, 10) * self.tendency self.base_temperature = truncate( old_factor * self.base_temperature + new_factor * new_base_temperature, -20, 40) # Calculate all temperature factors with their weights. # there will be max 8 degrees warmer in a day day_heat = 8 * (sin(self.basetime / 230 + 300) / 2 + 0.5) sun_heat = 2 * self.weather['light'] wind_chill = 5 * self.weather['wind'] rain_chill = 3 * self.weather['rain'] new_temp = (self.base_temperature + day_heat + sun_heat - wind_chill - rain_chill) old_factor = 0.3 new_factor = 1 - old_factor self.weather['temperature'] = truncate( old_factor * self.weather['temperature'] + new_factor * new_temp, -20, 40)
def action_less_heating(self): """Action to be taken by RL-agent. Decrease heating level.""" self.action_penalty = 1 if \ self.devices_settings['heating_lvl'] == 0 else 0 self.devices_settings['heating_lvl'] = round( truncate(self.devices_settings['heating_lvl'] - self.influence), 4)
def action_more_cooling(self): """Action to be taken by RL-agent. Increase cooling level.""" self.action_penalty = 1 if \ self.devices_settings['cooling_lvl'] == 1 else 0 self.devices_settings['cooling_lvl'] = round( truncate(self.devices_settings['cooling_lvl'] + self.influence), 4)
def _calculate_temperature(self, outside_temp): """Updates the inside sensors with new temperature value. The new value depends on the last temperature, the outside temperature, house's isolation factor, and levels of heating and cooling devices. Value is truncated to <-20, 40> interval and this interval is assumed in the environment's normalization method - HouseEnergyEnvironment.serialize_state(). Args: outside_temp(numeric): Unnormalized value of registered outside temperature Note: Currently, the sensor has no impact on the registered value, so every sensor would register the same value. """ for data in self.inside_sensors.values(): last_inside_temp = data['temperature'] temp_delta = (outside_temp - last_inside_temp) \ * (1 - self.house_isolation_factor) new_inside_temp = last_inside_temp \ + self.timeframe \ * (temp_delta + self.devices_settings['heating_lvl'] - self.devices_settings['cooling_lvl']) / 5 data['temperature_delta'] = new_inside_temp - last_inside_temp data['temperature'] = truncate(new_inside_temp, -20, 40)
def _calculate_light(self, outside_illumination): """Updates the inside sensors with new light value. Final light is normalized to <0, 1> interval and depends on the light device level, curtains level, outside light level and house_light_factor, which describes how much illumination 'enters' the house. Args: outside_illumination(numeric): Registered value of outside illumination Note: Currently, the sensor has no impact on the registered value, so every sensor would register the same value. """ for data in self.inside_sensors.values(): outside_light = (outside_illumination * self.house_light_factor) \ * (1 - self.devices_settings['curtains_lvl']) inside_light = self.devices_settings['light_lvl'] \ * self.max_led_illumination final_light = (outside_light + inside_light) \ / self.max_led_illumination data['light'] = truncate(final_light)
def apertium_wiki(phenny, origterm, to_nick=None): term, section = wiki.parse_term(origterm) w = wiki.Wiki(endpoints, None) match = w.search(term) if not match: phenny.say('Can\'t find anything in the Apertium Wiki for "{0}".'.format(term)) return snippet, url = wiki.extract_snippet(match, section) if to_nick: phenny.say(truncate(snippet, to_nick + ', "{}" - ' + url)) else: phenny.say(truncate(snippet, '"{}" - ' + url))
def apertium_wiki(phenny, origterm, to_nick=None): term, section = wiki.parse_term(origterm) w = wiki.Wiki(endpoints, None) match = w.search(term) if not match: phenny.say('Can\'t find anything in the Apertium Wiki for "{0}".'.format(term)) return snippet, url = wiki.extract_snippet(match, section) if to_nick: phenny.say(truncate(snippet, to_nick + ', "{}" - ' + url)) else: phenny.say(truncate(snippet, '"{}" - ' + url))
def retrieve_commit(phenny, input): '''Retreive commit information for a given repository and revision. This command is called as 'begiak: info <repo> <rev>'.''' # get repo and rev with regex data = input.group(1).split(' ') if len(data) != 2: phenny.reply("Invalid number of parameters.") return repo = data[0] rev = data[1] if repo in phenny.config.svn_repositories: # we don't handle SVN; see modules/svnpoller.py for that return if repo not in phenny.config.git_repositories: phenny.reply("That repository is not monitored by me!") return try: info, url = get_commit_info(phenny, repo, rev) except: phenny.reply("Invalid revision value!") return # the * is for unpacking msg = generate_report(repo, *info) # the URL is truncated so that it has at least 6 sha characters url = url[:url.rfind('/') + 7] phenny.say(truncate(msg, '{} ' + url))
def _calculate_light(self): """Light is calculated from sun and clouds""" clouds_factor = 0.7 self.weather['light'] = truncate(self.weather['sun'] - (self.weather['clouds'] * clouds_factor))
def retrieve_commit(phenny, input): '''Retreive commit information for a given repository and revision. This command is called as 'begiak: info <repo> <rev>'.''' # get repo and rev with regex data = input.group(1).split(' ') if len(data) != 2: phenny.reply("Invalid number of parameters.") return repo = data[0] rev = data[1] if repo in phenny.config.svn_repositories: # we don't handle SVN; see modules/svnpoller.py for that return if repo not in phenny.config.git_repositories: phenny.reply("That repository is not monitored by me!") return try: info, url = get_commit_info(phenny, repo, rev) except: phenny.reply("Invalid revision value!") return # the * is for unpacking msg = generate_report(repo, *info) # the URL is truncated so that it has at least 6 sha characters url = url[:url.rfind('/') + 7] phenny.say(truncate(msg, '{} ' + url))
def send_email(to, subject, body, signoff=None): if to: logging.info("Sending email to %s" % (to)) subject = EMAIL_PREFIX + subject subject = tools.truncate(subject, 40) if signoff: body = body + signoff deferred.defer(mail.send_mail, to=to, sender=SENDER_EMAIL, subject=subject, body=body)
def format_email(e, list_name): subject = e['Subject'] subject = re.sub(r"(=\?.*\?=)(?!$)", r"\1 ", subject) subject = str(make_header(decode_header(subject))) subject = subject.replace('['+list_name.capitalize()+'] ', '') # message = '{}: {} * {} * {}'.format(list_name, obfuscate_address(e['From']), subject, strip_reply_lines(e)) message = '{}: {}: {}'.format(list_name, obfuscate_address(e['From']), subject) return truncate(message)
def send_email(to, subject, body, signoff=None): if to: subject = EMAIL_PREFIX + subject subject = tools.truncate(subject, 40) if signoff: body = body + signoff deferred.defer(mail.send_mail, to=to, sender=SENDER_EMAIL, subject=subject, body=body)
def format_email(e, list_name): subject = e['Subject'] subject = re.sub(r"(=\?.*\?=)(?!$)", r"\1 ", subject) subject = str(make_header(decode_header(subject))) subject = subject.replace('[' + list_name.capitalize() + '] ', '') # message = '{}: {} * {} * {}'.format(list_name, obfuscate_address(e['From']), subject, strip_reply_lines(e)) message = '{}: {}: {}'.format(list_name, obfuscate_address(e['From']), subject) return truncate(message)
def _calculate_accumulated_energy(self, outside_illumination): """Calculates new value of energy accumulated in the battery Args: outside_illumination(numeric): Registered value of outside illumination """ acc = outside_illumination * self.max_pv_absorption * self.timeframe self.battery['delta'] = acc self.battery['current'] = truncate(arg=(acc + self.battery['current']), upper=self.battery['max'])
def format_email(e, list_name): subject = decode_mime_utf(e['Subject']) subject = subject.replace('[' + list_name.capitalize() + '] ', '') from_address = decode_mime_utf(e['From']) # message = '{}: {} * {} * {}'.format(list_name, obfuscate_address(e['From']), subject, strip_reply_lines(e)) message = '{}: {}: {}'.format(list_name, obfuscate_address(from_address), subject) return truncate(message)
def recentcommits(phenny, input): """List the most recent SVN commits.""" print("POLLING recent") for repo in phenny.config.svn_repositories: #phenny.say("{}: {}".format(repo, phenny.config.svn_repositories[repo])) poller = SVNPoller(repo, phenny.config.svn_repositories[repo]) # for (msg, revisions) in pollers[repo].check(phenny.revisions): rev = poller.get_last_revision() msg = poller.generateReport(rev, True) url = poller.sourceforgeURL(rev) phenny.say(truncate(msg, '{} ' + url)) print("POLLED recent")
def wikipedia(phenny, origterm, lang, to_user=None): if not origterm: return phenny.say('Perhaps you meant ".wik Zen"?') origterm = origterm.strip() lang = lang.strip() term, section = wiki.parse_term(origterm) w = wiki.Wiki(endpoints, lang) match = w.search(term) if not match: phenny.say('Can\'t find anything in Wikipedia for "{0}".'.format(origterm)) return snippet, url = wiki.extract_snippet(match, section) if to_user: phenny.say(truncate(snippet, to_user + ', "{}" - ' + url)) else: phenny.say(truncate(snippet, '"{}" - ' + url))
def action_less_light(self): """ Action to be taken by RL-agent. Decrease lights level. Note that this action uses 2 times smaller influence as agent needs a bit more precision with the light settings. """ self.action_penalty = 1 if \ self.devices_settings['light_lvl'] == 0 else 0.05 self.devices_settings['light_lvl'] = round( truncate(self.devices_settings['light_lvl'] - self.influence / 2), 4)
def get_recent_commit(phenny, input): '''Get recent commit information for each repository Begiak monitors. This command is called as 'begiak: recent'.''' for repo in phenny.config.git_repositories: html = web.get(phenny.config.git_repositories[repo] + '/commits') data = json.loads(html) # the * is for unpacking info, url = get_commit_info(phenny, repo, data[0]['sha']) msg = generate_report(repo, *info) # the URL is truncated so that it has at least 6 sha characters url = url[:url.rfind('/') + 7] phenny.say(truncate(msg, '{} ' + url))
def get_recent_commit(phenny, input): '''Get recent commit information for each repository Begiak monitors. This command is called as 'begiak: recent'.''' for repo in phenny.config.git_repositories: html = web.get(phenny.config.git_repositories[repo] + '/commits') data = json.loads(html) # the * is for unpacking info, url = get_commit_info(phenny, repo, data[0]['sha']) msg = generate_report(repo, *info) # the URL is truncated so that it has at least 6 sha characters url = url[:url.rfind('/') + 7] phenny.say(truncate(msg, '{} ' + url))
def wikipedia(phenny, origterm, lang, to_user=None): if not origterm: return phenny.say('Perhaps you meant ".wik Zen"?') origterm = origterm.strip() lang = lang.strip() term, section = wiki.parse_term(origterm) w = wiki.Wiki(endpoints, lang) match = w.search(term) if not match: phenny.say( 'Can\'t find anything in Wikipedia for "{0}".'.format(origterm)) return snippet, url = wiki.extract_snippet(match, section) if to_user: phenny.say(truncate(snippet, to_user + ', "{}" - ' + url)) else: phenny.say(truncate(snippet, '"{}" - ' + url))
def action_curtains_up(self): """ Action to be taken by RL-agent. Increase the curtains level. Note that this action uses 2 times smaller influence as agent needs a bit more precision with the light settings. There is a small penalty for using this action, to prevent it from being used as an action_nop. """ self.action_penalty = 1 if \ self.devices_settings['curtains_lvl'] == 0 else 0.05 self.devices_settings['curtains_lvl'] = round( truncate(self.devices_settings['curtains_lvl'] - self.influence / 2), 4)
def _calculate_sun(self): """Sun is calculated as a sinus between day start and end hour. Amplitude is set once per episode, and varies between <0.5, 1> """ daystart = self.config['env']['day_start'] dayend = self.config['env']['day_end'] daylen = dayend - daystart sun = 0 if daystart <= self.basetime <= dayend: sun = truncate(sin((self.basetime - daystart) * pi / daylen)) *\ self.sun_amplitude self.weather['sun'] = sun
def recentcommits(phenny, input): """List the most recent SVN commits.""" print("POLLING!!!!") if phenny.config.svn_repositories is None: phenny.say( "SVN module cannot function without repositories being set in the config file!" ) return for repo in phenny.config.svn_repositories: #phenny.say("{}: {}".format(repo, phenny.config.svn_repositories[repo])) poller = SVNPoller(repo, phenny.config.svn_repositories[repo]) #for (msg, revisions) in pollers[repo].check(phenny.revisions): rev = poller.get_last_revision() msg = poller.generateReport(rev, True) url = poller.sourceforgeURL(rev) phenny.say(truncate(msg, '{} ' + url))
def vtluug(phenny, input): """.vtluug <term> - Look up something on the VTLUUG wiki.""" origterm = input.group(1) if not origterm: return phenny.say('Perhaps you meant ".vtluug VT-Wireless"?') term, section = wiki.parse_term(origterm) w = wiki.Wiki(endpoints, None) match = w.search(term) if not match: phenny.say( 'Can\'t find anything in the VTLUUG Wiki for "{0}".'.format(term)) return snippet, url = wiki.extract_snippet(match, section) phenny.say(truncate(snippet, '"{}" - ' + url))
def retrieve_commit_svn(phenny, input): data = input.group(1).split(' ') if len(data) != 2: phenny.reply("Invalid number of parameters.") return repo = data[0] rev = data[1] if repo in phenny.config.git_repositories: return if repo not in phenny.config.svn_repositories: phenny.reply("That repository is not monitored by me!") return poller = SVNPoller(repo, phenny.config.svn_repositories[repo]) msg = poller.generateReport(rev, True) url = poller.sourceforgeURL(rev) phenny.say(truncate(msg, '{} ' + url))
def retrieve_commit_svn(phenny, input): data = input.group(1).split(' ') if len(data) != 2: phenny.reply("Invalid number of parameters.") return repo = data[0] rev = data[1] if repo in phenny.config.git_repositories: return if repo not in phenny.config.svn_repositories: phenny.reply("That repository is not monitored by me!") return poller = SVNPoller(repo, phenny.config.svn_repositories[repo]) msg = poller.generateReport(rev, True) url = poller.sourceforgeURL(rev) phenny.say(truncate(msg, '{} ' + url))
def search(phenny, input): if not input.group(1): return phenny.reply('.search for what?') query = input.group(1) if not is_up('https://api.duckduckgo.com'): return phenny.say('Sorry, DuckDuckGo API is down.') r = requests.get(ddg_uri + query, timeout=REQUEST_TIMEOUT).json() try: answer = r['AbstractText'] answer_url = r['AbstractURL'] if answer == '': answer = r['RelatedTopics'][0]['Text'] answer_url = r['RelatedTopics'][0]['FirstURL'] if answer == '': return phenny.say('Sorry, no result.') except: return phenny.say('Sorry, no result.') # Removes html tags, if exist answer = re.sub('<.+?>', '', answer) phenny.say(truncate(answer, '{} - ' + answer_url))
def draw_weather_widget(self): """Draws weather widget Widget has timer animation and five weather indicators: temperature and sun / wind / clouds / rain intensity. """ # bg x = y = self.margin xmax = self.width * 3 // 7 - self.margin ymax = self.height - self.margin w = xmax - x h = ymax - y pygame.draw.rect(self.screen, self.colors['white'], (x, y, w, h)) # small rects for i in range(1, 5): pygame.draw.rect(self.screen, self.colors['weather{}'.format(i)], (x, y + (0.5 + i/10) * h, w, 0.11 * h)) # circle radius = 0.19 circle_center_x = x + 0.5 * w circle_center_y = y + 0.22 * h cur_time = self.data['Daytime'] time_color_factor = cur_time / 720 if cur_time <= 720 else\ 2 - cur_time / 720 color_daytime = pygame.Color( truncate(int(time_color_factor * 45 + 235), 0, 255), truncate(int(time_color_factor * 30 + 235), 0, 255), truncate(int(time_color_factor * 30 + 235), 0, 255) ) for _radius, _color in ((radius + 0.01, self.colors['weather1']), (radius - 0.01, color_daytime)): pygame.draw.circle( self.screen, _color, (int(circle_center_x), int(circle_center_y)), int(_radius * w) ) # time indicator (moving circle) pygame.draw.rect( self.screen, self.colors['weather1'], (int(circle_center_x - 0.01 * w), int(circle_center_y + radius * h * 0.7), 0.02 * w, 0.035 * h) ) daytime = self.data['Daytime'] _phi = (daytime / 1440) * 2 * math.pi + math.pi / 2 x_indicator = radius * math.cos(_phi) y_indicator = radius * math.sin(_phi) radius_indicator = 0.03 for _radius, _color in ((radius_indicator, 'weather1'), (radius_indicator - 0.01, 'weather5')): pygame.draw.circle( self.screen, self.colors[_color], (int(circle_center_x + x_indicator * w), int(circle_center_y + y_indicator * w)), int(_radius * w) ) # text - clock font_mono = pygame.font.Font( '../static/fonts/droid-sans-mono/DroidSansMono.ttf', int(0.05 * h) ) color_daytime_clock = pygame.Color( truncate(200 - int(time_color_factor * (200 - 221)), 0, 255), truncate(200 - int(time_color_factor * (200 - 207)), 0, 255), truncate(200 - int(time_color_factor * (200 - 179)), 0, 255), ) font_header = pygame.font.Font('../static/fonts/Lato/Lato-Regular.ttf', int(0.09 * h)) time = [int(x) for x in divmod(daytime, 60)] time = "{:02}:{:02}".format(*time) self.draw_text(time, circle_center_x, circle_center_y, color_daytime_clock, font_header, True) # text - blocks for _off, _data in enumerate(('Outside Light', 'Wind', 'Clouds', 'Rain')): _label = _data.upper() if _data == 'Outside Light': _label = 'SUN' self.draw_text("{:<13}{:>5.0f}%".format( _label, self.data[_data] * 100), x + 0.57 * w, y + (0.65 + _off / 10) * h, self.colors['font'], font_mono, True) # text - temperature self.draw_text("{:<3.1f}°C".format( self.data['Outside Temp'] ), x + 0.55 * w, y + 0.5 * h, self.colors['weather5'], font_header, True) # weather icons for _off, _data in enumerate(('016-sun', '013-wind', '015-cloud', '010-raining')): self.draw_icon('../static/icons/weather/{}.png'.format(_data), x + 0.1 * w, y + (0.65 + _off / 10) * h, 0.08 * h, 0.08 * h, self.colors['font'], True) self.draw_icon('../static/icons/weather/003-temperature.png', x + 0.3 * w, y + 0.49 * h, 0.1 * h, 0.1 * h, self.colors['weather5'], True) # day / night icon if 6 * 60 < daytime < 19 * 60: daynight_icon = '../static/icons/weather/016-sun.png' daynight_color = self.colors['weather1'] else: daynight_icon = '../static/icons/weather/004-moon.png' daynight_color = self.colors['weather3'] self.draw_icon(daynight_icon, x + 0.13 * w, y + 0.1 * h, 0.13 * h, 0.13 * h, daynight_color, True)
def do_POST_unsafe(self, data): '''Runs once per event. One repository. One event type.''' config = self.phenny.config default_channels = config.git_channels.get('*', config.channels) channels = default_channels # both commit reports and error reports messages = [] repo = '' event = None # handle GitHub triggers if 'GitHub' in self.headers['User-Agent']: event = self.headers['X-Github-Event'] user = data['sender']['login'] if 'repository' in data: repo = data['repository']['name'] elif 'organization' in data: repo = data['organization']['login'] + ' (org)' if config.git_events: full_name = data['repository']['full_name'] event_types = [] for key, value in config.git_events.items(): if fnmatch(full_name, key): event_types = value else: event_types = None event_in_config = False if 'action' in data: for event_type in event_types: if (event + '_' + data['action'] == event_type) or (event == event_type): event_in_config = True if (event_types is not None) and ((event not in event_types) and (not event_in_config)): return [], [] if config.git_channels: full_name = data['repository']['full_name'] channels = [] for key, value in config.git_channels.items(): if fnmatch(full_name, key): channels = value if event == 'commit_comment': commit = data['comment']['commit_id'][:7] url = data['comment']['html_url'] url = url[:url.rfind('/') + 7] action = data['action'] if action == 'deleted': template = '{:}: {:} * comment deleted on commit {:}: {:}' messages.append(template.format(repo, user, commit, url)) else: template = '{:}: {:} * comment {:} on commit {:}: {:} {:}' messages.append(truncate( data['comment']['body'], template.format(repo, user, action, commit, '{}', url) )) elif event == 'create' or event == 'delete': template = '{:}: {:} * {:} {:} {:}d {:}' ref = data['ref'] type_ = data['ref_type'] messages.append(template.format(repo, user, type_, ref, event)) elif event == 'fork': template = '{:}: {:} forked this repo {:}' url = data['forkee']['html_url'] messages.append(template.format(repo, user, url)) elif event == 'issue_comment': if 'pull_request' in data['issue']: url = data['issue']['pull_request']['html_url'] text = 'pull request' else: url = data['issue']['html_url'] text = 'issue' number = data['issue']['number'] action = data['action'] if action == 'deleted': template = '{:}: {:} * comment deleted on {:} #{:}: {:}' messages.append(template.format(repo, user, text, number, url)) else: template = '{:}: {:} * comment {:} on {:} #{:}: {:} {:}' messages.append(truncate( data['comment']['body'], template.format(repo, user, action, text, number, '{}', url) )) elif event == 'issues': template = '{:}: {:} * issue #{:} "{:}" {:} {:} {:}' number = data['issue']['number'] title = data['issue']['title'] action = data['action'] url = data['issue']['html_url'] opt = '' if data['issue']['assignee']: opt += 'assigned to ' + data['issue']['assignee']['login'] elif 'label' in data: opt += 'with ' + data['label']['name'] messages.append(template.format(repo, user, number, title, action, opt, url)) elif event == 'member': template = '{:}: {:} * user {:} {:} as collaborator {:}' new_user = data['member']['login'] action = data['action'] messages.append(template.format(repo, user, new_user, action)) elif event == 'membership': template = '{:}: user {:} {:} {:} {:} {:} {:}' new_user = data['member']['login'] action = data['action'] prep = ['to', 'from'][int(action == 'removed')] scope = data['scope'] name = data['team']['name'] messages.append(template.format(repo, new_user, action, prep, scope, name)) elif event == 'pull_request': template = '{:}: {:} * pull request #{:} "{:}" {:} {:} {:}' number = data['number'] title = data['pull_request']['title'] action = data['action'] url = data['pull_request']['html_url'] opt = '' if data['pull_request']['assignee']: opt = 'to ' + data['pull_request']['assignee'] messages.append(template.format(repo, user, number, title, action, opt, url)) elif event == 'pull_request_review_comment': template = '{:}: {:} * review comment deleted on pull request #{:}: {:}' number = data['pull_request']['number'] url = data['comment']['html_url'] action = data['action'] if action == 'deleted': messages.append(template.format(repo, user, number, url)) else: template = '{:}: {:} * review comment {:} on pull request #{:}: {:} {:}' messages.append(truncate( data['comment']['body'], template.format(repo, user, action, number, '{}', url) )) elif event == 'push': pusher_name = data['pusher']['name'] if pusher_name in config.gitbots: return [], [] ref = data['ref'].split('/')[-1] repo_fullname = data['repository']['full_name'] fork = data['repository']['fork'] if ref != 'master' or fork: try: channels = config.branch_channels[repo_fullname][ref] except: return [], [] template = '{:}: {:} [ {:} ] {:}: {:}' out_messages = [] out_commithashes = [] out_files = set() for commit in data['commits']: out_commithashes.append(commit['id'][:7]) out_files.update(commit['modified'], commit['added']) out_messages.append(commit['message']) messages.append(truncate(" * ".join(out_messages), template.format( data['repository']['name'], pusher_name, ' '.join(out_commithashes), ', '.join(out_files), '{}' ))) elif event == 'release': template = '{:}: {:} * release {:} {:} {:}' tag = data['release']['tag_name'] action = data['action'] url = data['release']['html_url'] messages.append(template.format(repo, user, tag, action, url)) elif event == 'repository': template = 'new repository {:} {:} by {:} {:}' name = data['repository']['name'] action = data['action'] url = data['repository']['url'] messages.append(template.format(name, action, user, url, url)) elif event == 'team_add': template = 'repository {:} added to team {:} {:}' name = data['repository']['full_name'] team = data['team']['name'] messages.append(template.format(name, team)) elif event == 'ping': template = 'ping from {:}, org: {:}' if 'organization' in data: org = data['organization'] else: org = "no org specified!" sender = data['sender']['login'] messages.append(template.format(sender, org)) elif 'Jenkins' in self.headers['User-Agent']: messages.append('Jenkins: {}'.format(data['message'])) # not github or Jenkins elif "commits" in data: for commit in data['commits']: try: if "author" in commit: # for bitbucket message = self.return_data("bitbucket", data, commit) messages.append(message) else: # we don't know which site message = "unsupported data: " + str(commit) messages.append(message) except Exception: logger.warning("unsupported data: " + str(commit)) if not messages: # we couldn't get anything channels = default_channels if event: messages.append("Don't know about '" + event + "' events") else: messages.append("Unable to deal with unknown event") return channels, messages
def test_truncate_short(self): text = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut." self.assertEqual(tools.truncate(text, max_length=100), text)
def test_truncate_long(self): text = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut " \ "labore et dolore magna aliquyam erat, sed diam voluptua." truncated = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt..." self.assertEqual(tools.truncate(text, max_length=100), truncated)
def do_POST_unsafe(self, data): '''Runs once per event. One repository. One event type.''' config = self.phenny.config default_channels = config.git_channels.get('*', config.channels) channels = default_channels # both commit reports and error reports messages = [] repo = '' event = None # handle GitHub triggers if 'GitHub' in self.headers['User-Agent']: event = self.headers['X-Github-Event'] user = data['sender']['login'] if 'repository' in data: repo = data['repository']['name'] elif 'organization' in data: repo = data['organization']['login'] + ' (org)' if config.git_events: full_name = data['repository']['full_name'] event_types = [] for key, value in config.git_events.items(): if fnmatch(full_name, key): event_types = value else: event_types = None event_in_config = False if 'action' in data: for event_type in event_types: if (event + '_' + data['action'] == event_type) or (event == event_type): event_in_config = True if (event_types is not None) and ((event not in event_types) and (not event_in_config)): return [], [] if config.git_channels: full_name = data['repository']['full_name'] channels = [] for key, value in config.git_channels.items(): if fnmatch(full_name, key): channels = value if event == 'commit_comment': commit = data['comment']['commit_id'][:7] url = data['comment']['html_url'] url = url[:url.rfind('/') + 7] action = data['action'] if action == 'deleted': template = '{:}: {:} * comment deleted on commit {:}: {:}' messages.append(template.format(repo, user, commit, url)) else: template = '{:}: {:} * comment {:} on commit {:}: {:} {:}' messages.append( truncate( data['comment']['body'], template.format(repo, user, action, commit, '{}', url))) elif event == 'create' or event == 'delete': template = '{:}: {:} * {:} {:} {:}d {:}' ref = data['ref'] type_ = data['ref_type'] messages.append(template.format(repo, user, type_, ref, event)) elif event == 'fork': template = '{:}: {:} forked this repo {:}' url = data['forkee']['html_url'] messages.append(template.format(repo, user, url)) elif event == 'issue_comment': if 'pull_request' in data['issue']: url = data['issue']['pull_request']['html_url'] text = 'pull request' else: url = data['issue']['html_url'] text = 'issue' number = data['issue']['number'] action = data['action'] if action == 'deleted': template = '{:}: {:} * comment deleted on {:} #{:}: {:}' messages.append( template.format(repo, user, text, number, url)) else: template = '{:}: {:} * comment {:} on {:} #{:}: {:} {:}' messages.append( truncate( data['comment']['body'], template.format(repo, user, action, text, number, '{}', url))) elif event == 'issues': template = '{:}: {:} * issue #{:} "{:}" {:} {:} {:}' number = data['issue']['number'] title = data['issue']['title'] action = data['action'] url = data['issue']['html_url'] opt = '' if data['issue']['assignee']: opt += 'assigned to ' + data['issue']['assignee']['login'] elif 'label' in data: opt += 'with ' + data['label']['name'] messages.append( template.format(repo, user, number, title, action, opt, url)) elif event == 'member': template = '{:}: {:} * user {:} {:} as collaborator {:}' new_user = data['member']['login'] action = data['action'] messages.append(template.format(repo, user, new_user, action)) elif event == 'membership': template = '{:}: user {:} {:} {:} {:} {:} {:}' new_user = data['member']['login'] action = data['action'] prep = ['to', 'from'][int(action == 'removed')] scope = data['scope'] name = data['team']['name'] messages.append( template.format(repo, new_user, action, prep, scope, name)) elif event == 'pull_request': template = '{:}: {:} * pull request #{:} "{:}" {:} {:} {:}' number = data['number'] title = data['pull_request']['title'] action = data['action'] url = data['pull_request']['html_url'] opt = '' if data['pull_request']['assignee']: opt = 'to ' + data['pull_request']['assignee'] messages.append( template.format(repo, user, number, title, action, opt, url)) elif event == 'pull_request_review_comment': template = '{:}: {:} * review comment deleted on pull request #{:}: {:}' number = data['pull_request']['number'] url = data['comment']['html_url'] action = data['action'] if action == 'deleted': messages.append(template.format(repo, user, number, url)) else: template = '{:}: {:} * review comment {:} on pull request #{:}: {:} {:}' try: blacklist_pull_request_comment_users = config.blacklist_pull_request_comment_users except AttributeError: blacklist_pull_request_comment_users = () if user not in blacklist_pull_request_users: messages.append( truncate( data['comment']['body'], template.format(repo, user, action, number, '{}', url))) elif event == 'push': pusher_name = data['pusher']['name'] if pusher_name in config.gitbots: return [], [] ref = data['ref'].split('/')[-1] repo_fullname = data['repository']['full_name'] fork = data['repository']['fork'] if ref != 'master' or fork: try: channels = config.branch_channels[repo_fullname][ref] except: return [], [] template = '{:}: {:} [ {:} ] {:}: {:}' out_messages = [] out_commithashes = [] out_files = set() for commit in data['commits']: out_commithashes.append(commit['id'][:7]) out_files.update(commit['modified'], commit['added']) out_messages.append(commit['message']) messages.append( truncate( " * ".join(out_messages), template.format(data['repository']['name'], pusher_name, ' '.join(out_commithashes), ', '.join(out_files), '{}'))) elif event == 'release': template = '{:}: {:} * release {:} {:} {:}' tag = data['release']['tag_name'] action = data['action'] url = data['release']['html_url'] messages.append(template.format(repo, user, tag, action, url)) elif event == 'repository': template = 'new repository {:} {:} by {:} {:}' name = data['repository']['name'] action = data['action'] url = data['repository']['url'] messages.append(template.format(name, action, user, url, url)) elif event == 'team_add': template = 'repository {:} added to team {:} {:}' name = data['repository']['full_name'] team = data['team']['name'] messages.append(template.format(name, team)) elif event == 'ping': template = 'ping from {:}, org: {:}' if 'organization' in data: org = data['organization'] else: org = "no org specified!" sender = data['sender']['login'] messages.append(template.format(sender, org)) elif 'Jenkins' in self.headers['User-Agent']: messages.append('Jenkins: {}'.format(data['message'])) # not github or Jenkins elif "commits" in data: for commit in data['commits']: try: if "author" in commit: # for bitbucket message = self.return_data("bitbucket", data, commit) messages.append(message) else: # we don't know which site message = "unsupported data: " + str(commit) messages.append(message) except Exception: logger.warning("unsupported data: " + str(commit)) if not messages: # we couldn't get anything channels = default_channels if event: messages.append("Don't know about '" + event + "' events") else: messages.append("Unable to deal with unknown event") return channels, messages