def github_url(self): """Return the Github repository URL.""" if "https://github.com" not in self.action_url: raise Exception("not a github url") _repo_name = repo_name(self.action_url) _org_name = org_name(self.action_url) return f"https://github.com/{_org_name}/{_repo_name}"
def handle_avatar(request, _org_name='', add_gitcoincologo=False): from dashboard.models import Profile icon_size = (215, 215) if _org_name: _org_name = _org_name.replace('@', '') if is_blocked(_org_name) or is_deleted_account(_org_name): return get_err_response(request, blank_img=(_org_name == 'Self')) if _org_name: try: profile = Profile.objects.prefetch_related('avatar_baseavatar_related')\ .filter(handle=_org_name.lower()).first() if profile and profile.active_avatar_nocache: avatar_file, content_type = profile.active_avatar_nocache.determine_response( request.GET.get('email', False) ) if avatar_file: return HttpResponse(avatar_file, content_type=content_type) except Exception as e: logger.error('Handle Avatar - Exception: (%s) - Handle: (%s)', str(e), _org_name) logger.exception(e) # default response # params repo_url = request.GET.get('repo', False) if not _org_name and (not repo_url or 'github.com' not in repo_url): return get_err_response(request, blank_img=(_org_name == 'Self')) try: # get avatar of repo if not _org_name: _org_name = org_name(repo_url) filepath = get_avatar(_org_name) if isinstance(filepath, JsonResponse): raise AvatarNotFoundException('no avatar found') # new image img = Image.new('RGBA', icon_size, (255, 255, 255)) # execute avatar = Image.open(filepath, 'r').convert("RGBA") avatar = ImageOps.fit(avatar, icon_size, Image.ANTIALIAS) offset = 0, 0 img.paste(avatar, offset, avatar) # Determine if we should add the Gitcoin logo if add_gitcoincologo and _org_name != 'gitcoinco': img = add_gitcoin_logo_blend(avatar, icon_size) response = HttpResponse(content_type='image/png') img.save(response, 'PNG') return response except AvatarNotFoundException: return get_err_response(request, blank_img=(_org_name == 'Self')) except (AttributeError, IOError, SyntaxError) as e: logger.error('Handle Avatar - Response error: (%s) - Handle: (%s)', str(e), _org_name) logger.exception(e) return get_err_response(request, blank_img=(_org_name == 'Self'))
def handle(self, *args, **options): notifications = get_gh_notifications() print('Notifications Count: ', notifications.totalCount) for notification in notifications: if notification.reason == 'mention': try: url = notification.subject.url url = url.replace('/repos', '') url = url.replace('//api.github', '//github') latest_comment_url = notification.subject.latest_comment_url _org_name = org_name(url) _repo_name = repo_name(url) _issue_number = issue_number(url) _comment_id = latest_comment_url.split('/')[-1] comment = get_issue_comments(_org_name, _repo_name, _issue_number, _comment_id) does_mention_gitcoinbot = settings.GITHUB_API_USER in comment.get( 'body', '') if comment.get('message', '') == "Not Found": print("comment was not found") elif not does_mention_gitcoinbot: print("does not mention gitcoinbot") else: comment_from = comment['user']['login'] num_reactions = comment['reactions']['total_count'] print(_org_name, _repo_name, _issue_number, _comment_id, num_reactions, comment_from) is_from_gitcoinbot = settings.GITHUB_API_USER in comment_from if num_reactions == 0 and not is_from_gitcoinbot: print("unprocessed") post_issue_comment_reaction( _org_name, _repo_name, _comment_id, 'heart') except Exception as e: logging.exception(e) print(e)
def issue_details(request): """Determine the Github issue keywords of the specified Github issue or PR URL. Todo: * Modify the view to only use the Github API (remove BeautifulSoup). * Simplify the view logic. Returns: JsonResponse: A JSON response containing the Github issue or PR keywords. """ from dashboard.utils import clean_bounty_url response = {} token = request.GET.get('token', None) url = request.GET.get('url') url_val = URLValidator() hackathon_slug = request.GET.get('hackathon_slug') duplicates = request.GET.get('duplicates', False) network = request.GET.get('network', 'mainnet') if hackathon_slug: sponsor_profiles = HackathonEvent.objects.filter(slug__iexact=hackathon_slug).prefetch_related('sponsor_profiles').values_list('sponsor_profiles__handle', flat=True) org_issue = org_name(url).lower() if org_issue not in sponsor_profiles: message = 'This issue is not under any sponsor repository' return JsonResponse({'status':'false','message':message}, status=404) if duplicates: if Bounty.objects.filter(github_url=url, network=network).exists(): message = 'Bounty already exists for this github issue' response = { 'status': 422, 'message': message } return JsonResponse(response, status=422) try: url_val(url) except ValidationError: response['message'] = 'invalid arguments' return JsonResponse(response) if url.lower()[:19] != 'https://github.com/': response['message'] = 'invalid arguments' return JsonResponse(response) url_dict = get_url_dict(clean_bounty_url(url)) try: if url_dict: response = get_gh_issue_details(token=token, **url_dict) else: response['message'] = 'could not parse Github url' except Exception as e: logger.warning(e) message = 'could not pull back remote response' return JsonResponse({'status':'false','message':message}, status=404) return JsonResponse(response)
def handle(self, *args, **options): notifications = get_notifications() try: print('Notifications Count: ', notifications.totalCount) for notification in notifications: if hasattr(notification, '_subject') and notification.reason == 'mention': try: url = notification.subject.url url = url.replace('/repos', '') url = url.replace('//api.github', '//github') latest_comment_url = notification.subject.latest_comment_url if latest_comment_url is None: print("no latest comment url") continue _org_name = org_name(url) _repo_name = repo_name(url) _issue_number = issue_number(url) if not latest_comment_url: continue _comment_id = latest_comment_url.split('/')[-1] comment = get_issue_comments(_org_name, _repo_name, _issue_number, _comment_id) does_mention_gitcoinbot = settings.GITHUB_API_USER in comment.body if isinstance(comment, dict) and comment.get( 'message', '') == 'Not Found': print("comment was not found") elif not does_mention_gitcoinbot: print("does not mention gitcoinbot") else: comment_from = comment.user.login num_reactions = comment.get_reactions().totalCount print(_org_name, _repo_name, _issue_number, _comment_id, num_reactions, comment_from) is_from_gitcoinbot = settings.GITHUB_API_USER in comment_from if num_reactions == 0 and not is_from_gitcoinbot: print("unprocessed") post_issue_comment_reaction( _org_name, _repo_name, _comment_id, 'heart') except RateLimitExceededException as e: logging.debug(e) print(e) time.sleep(60) except Exception as e: logging.exception(e) print(e) except RateLimitExceededException as e: logging.debug(e) print(e) except AttributeError as e: logging.debug(e) print(e)
def handle_avatar(request, _org_name='', add_gitcoincologo=False): from dashboard.models import Profile icon_size = (215, 215) if _org_name: try: profile = Profile.objects.select_related('avatar').get( handle__iexact=_org_name) if profile.avatar: if profile.avatar.use_github_avatar and profile.avatar.png: return HttpResponse(profile.avatar.png.file, content_type='image/png') elif profile.avatar.svg and not profile.avatar.use_github_avatar: return HttpResponse(profile.avatar.svg.file, content_type='image/svg+xml') except Exception as e: logger.error(e) # default response # params repo_url = request.GET.get('repo', False) if not _org_name and (not repo_url or 'github.com' not in repo_url): return get_err_response(request, blank_img=(_org_name == 'Self')) try: # get avatar of repo if not _org_name: _org_name = org_name(repo_url) filepath = get_avatar(_org_name) # new image img = Image.new('RGBA', icon_size, (255, 255, 255)) # execute avatar = Image.open(filepath, 'r').convert("RGBA") avatar = ImageOps.fit(avatar, icon_size, Image.ANTIALIAS) offset = 0, 0 img.paste(avatar, offset, avatar) # Determine if we should add the Gitcoin logo if add_gitcoincologo and _org_name != 'gitcoinco': img = add_gitcoin_logo_blend(avatar, icon_size) response = HttpResponse(content_type='image/png') img.save(response, 'PNG') return response except (AttributeError, IOError, SyntaxError) as e: logger.error(e) return get_err_response(request, blank_img=(_org_name == 'Self'))
def maybe_market_to_user_discord(bounty, event_name): """Send a Discord message to the user's discord channel for the specified Bounty. Args: bounty (dashboard.models.Bounty): The Bounty to be marketed. event_name (str): The name of the event. Returns: bool: Whether or not the Discord notification was sent successfully. """ from dashboard.models import Profile if bounty.get_natural_value() < 0.0001: return False if bounty.network != settings.ENABLE_NOTIFICATIONS_ON_NETWORK: return False msg = build_message_for_integration(bounty, event_name) if not msg: return False url = bounty.github_url sent = False try: repo = org_name(url) + '/' + repo_name(url) subscribers = Profile.objects.filter(discord_repos__contains=[repo]) subscribers = subscribers & Profile.objects.exclude( discord_webhook_url='') for subscriber in subscribers: try: headers = {'Content-Type': 'application/json'} body = { "content": msg, "avatar_url": "https://gitcoin.co/static/v2/images/helmet.png" } discord_response = requests.post( subscriber.discord_webhook_url, headers=headers, json=body) if discord_response.status_code == 204: sent = True except Exception as e: print(e) except Exception as e: print(e) return sent
def maybe_market_to_user_slack(bounty, event_name): """Send a Slack message to the user's slack channel for the specified Bounty. Args: bounty (dashboard.models.Bounty): The Bounty to be marketed. event_name (str): The name of the event. Returns: bool: Whether or not the Slack notification was sent successfully. """ from dashboard.models import Profile if bounty.get_natural_value() < 0.0001: return False if bounty.network != settings.ENABLE_NOTIFICATIONS_ON_NETWORK: return False msg = build_message_for_integration(bounty, event_name) if not msg: return False url = bounty.github_url sent = False try: repo = org_name(url) + '/' + repo_name(url) subscribers = Profile.objects.filter(slack_repos__contains=[repo]) subscribers = subscribers & Profile.objects.exclude(slack_token='', slack_channel='') for subscriber in subscribers: try: sc = SlackClient(subscriber.slack_token) sc.api_call( "chat.postMessage", channel=subscriber.slack_channel, text=msg, icon_url=settings.GITCOIN_SLACK_ICON_URL, ) sent = True except Exception as e: print(e) except Exception as e: print(e) return sent
def org_name(self): from git.utils import org_name try: return org_name(self.reference_url) except Exception: return None
def test_org_name(self): """Test the github utility org_name method.""" assert org_name( 'https://github.com/gitcoinco/web/issues/1') == 'gitcoinco' assert org_name( 'https://github.com/gitcoinco/web/issues/1/') == 'gitcoinco'
def embed(request): # default response could_not_find = Image.new('RGBA', (1, 1), (0, 0, 0, 0)) err_response = HttpResponse(content_type="image/jpeg") could_not_find.save(err_response, "JPEG") # Get maxAge GET param if provided, else default on the small side max_age = int(request.GET.get('maxAge', 3600)) # params repo_url = request.GET.get('repo', False) if not repo_url or 'github.com' not in repo_url: return err_response try: badge = request.GET.get('badge', False) if badge: open_bounties = Bounty.objects.current() \ .filter( github_url__startswith=repo_url, network='mainnet', idx_status__in=['open'] ) tmpl = loader.get_template('svg_badge.txt') response = HttpResponse( tmpl.render({'bounties_count': open_bounties.count()}), content_type='image/svg+xml', ) patch_response_headers(response, cache_timeout=max_age) return response # get avatar of repo _org_name = org_name(repo_url) avatar = None filename = f"{_org_name}.png" filepath = 'assets/other/avatars/' + filename try: avatar = Image.open(filepath, 'r').convert("RGBA") except IOError: remote_user = get_user(_org_name) if not hasattr(remote_user, 'avatar_url'): return JsonResponse({'msg': 'invalid user'}, status=422) remote_avatar_url = remote_user.avatar_url r = requests.get(remote_avatar_url, stream=True) chunk_size = 20000 with open(filepath, 'wb') as fd: for chunk in r.iter_content(chunk_size): fd.write(chunk) avatar = Image.open(filepath, 'r').convert("RGBA") # make transparent datas = avatar.getdata() new_data = [] for item in datas: if item[0] == 255 and item[1] == 255 and item[2] == 255: new_data.append((255, 255, 255, 0)) else: new_data.append(item) avatar.putdata(new_data) avatar.save(filepath, "PNG") # get issues length = request.GET.get('len', 10) super_bounties = Bounty.objects.current() \ .filter( github_url__startswith=repo_url, network='mainnet', idx_status__in=['open', 'started', 'submitted'] ).order_by('-_val_usd_db') bounties = super_bounties[:length] # config bounty_height = 200 bounty_width = 572 font = 'assets/v2/fonts/futura/FuturaStd-Medium.otf' width = 1776 height = 576 # setup img = Image.new("RGBA", (width, height), (255, 255, 255)) draw = ImageDraw.Draw(img) black = (0, 0, 0) gray = (102, 102, 102) h1 = ImageFont.truetype(font, 36, encoding="unic") h2_thin = ImageFont.truetype(font, 36, encoding="unic") p = ImageFont.truetype(font, 24, encoding="unic") # background background_image = 'assets/v2/images/embed-widget/background.png' back = Image.open(background_image, 'r').convert("RGBA") offset = 0, 0 img.paste(back, offset) # repo logo icon_size = (184, 184) avatar.thumbnail(icon_size, Image.ANTIALIAS) offset = 195, 148 img.paste(avatar, offset, avatar) img_org_name = ImageDraw.Draw(img) img_org_name_size = img_org_name.textsize(_org_name, h1) img_org_name.multiline_text( align="left", xy=(287 - img_org_name_size[0] / 2, 360), text=_org_name, fill=black, font=h1, ) draw.multiline_text( align="left", xy=(110, 410), text="supports funded issues", fill=black, font=h1, ) # put bounty list in there i = 0 for bounty in bounties[:4]: i += 1 # execute line_size = 2 # Limit text to 28 chars text = f"{bounty.title_or_desc}" text = (text[:28] + '...') if len(text) > 28 else text x = 620 + (int((i - 1) / line_size) * (bounty_width)) y = 230 + (abs(i % line_size - 1) * bounty_height) draw.multiline_text(align="left", xy=(x, y), text=text, fill=black, font=h2_thin) unit = 'day' num = int(round((bounty.expires_date - timezone.now()).days, 0)) if num == 0: unit = 'hour' num = int( round((bounty.expires_date - timezone.now()).seconds / 3600 / 24, 0)) unit = unit + ("s" if num != 1 else "") draw.multiline_text( align="left", xy=(x, y - 40), text=f"Expires in {num} {unit}:", fill=gray, font=p, ) bounty_eth_background = Image.new("RGBA", (200, 56), (231, 240, 250)) bounty_usd_background = Image.new("RGBA", (200, 56), (214, 251, 235)) img.paste(bounty_eth_background, (x, y + 50)) img.paste(bounty_usd_background, (x + 210, y + 50)) tmp = ImageDraw.Draw(img) bounty_value_size = tmp.textsize( f"{round(bounty.value_true, 2)} {bounty.token_name}", p) draw.multiline_text( align="left", xy=(x + 100 - bounty_value_size[0] / 2, y + 67), text=f"{round(bounty.value_true, 2)} {bounty.token_name}", fill=(44, 35, 169), font=p, ) bounty_value_size = tmp.textsize( f"{round(bounty.value_in_usdt_now, 2)} USD", p) draw.multiline_text( align="left", xy=(x + 310 - bounty_value_size[0] / 2, y + 67), text=f"{round(bounty.value_in_usdt_now, 2)} USD", fill=(45, 168, 116), font=p, ) # blank slate if bounties.count() == 0: draw.multiline_text( align="left", xy=(760, 320), text= "No active issues. Post a funded issue at: https://gitcoin.co", fill=gray, font=h1, ) if bounties.count() != 0: text = 'Browse issues at: https://gitcoin.co/explorer' draw.multiline_text( align="left", xy=(64, height - 70), text=text, fill=gray, font=p, ) draw.multiline_text( align="left", xy=(624, 120), text="Recently funded issues:", fill=(62, 36, 251), font=p, ) _, value = summarize_bounties(super_bounties) value_size = tmp.textsize(value, p) draw.multiline_text( align="left", xy=(1725 - value_size[0], 120), text=value, fill=gray, font=p, ) line_table_header = Image.new("RGBA", (1100, 6), (62, 36, 251)) img.paste(line_table_header, (624, 155)) # Resize back to output size for better anti-alias img = img.resize((888, 288), Image.LANCZOS) # Return image with right content-type response = HttpResponse(content_type="image/png") img.save(response, "PNG") patch_response_headers(response, cache_timeout=max_age) return response except IOError as e: print(e) return err_response
def github_avatar_url(self): """Return the local avatar URL.""" _org_name = org_name(self.action_url) if _org_name: return f"{settings.BASE_URL}static/avatar/{_org_name}" return f"{settings.BASE_URL}funding/avatar?repo={self.github_url}&v=3"