Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
annndruha committed Jul 19, 2023
1 parent dc7f99e commit de19edc
Show file tree
Hide file tree
Showing 17 changed files with 59,893 additions and 27,479 deletions.
2 changes: 0 additions & 2 deletions .gitignore

This file was deleted.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Only for GitHub organizations repos issues (not for personal repos issues)
#### Setup

1. You need to create telegram bot via [BotFather](https://t.me/BotFather) and get bot token
2. Use your personal GitHub account or create another account, get [GitHub token](https://github.com/settings/tokens)
2. Use your personal GitHub account or create another account, get [GitHub token](https://github.com/settings/tokens).
Token scopes must include: `repo (full)`, `admin:org -> read:org`, `user -> read:user`, `project -> read:project`

3. Next set the docker environment secrets:
Expand Down
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,3 @@ gql[all]
aiohttp
pydantic
pydantic-settings
pydantic[dotenv]
26 changes: 16 additions & 10 deletions src/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,41 @@

import logging

from telegram.ext import ApplicationBuilder, CommandHandler, CallbackQueryHandler, MessageHandler
from telegram.ext import filters
from telegram.ext import ApplicationBuilder, CallbackQueryHandler, CommandHandler, MessageHandler, filters
from gql.transport.requests import log as requests_logger

from src.handlers import handler_button, handler_help, handler_md_guide, handler_message, handler_start
from src.settings import Settings
from src.handlers import handler_start, handler_help, handler_md_guide,\
handler_button, handler_message, native_error_handler

tg_log_handler = logging.FileHandler("issue_tgbot_telegram_updater.log")
tg_log_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
tg_logger = logging.getLogger('telegram.ext._updater')
tg_logger.propagate = False
tg_logger.addHandler(tg_log_handler)

requests_logger.setLevel(logging.WARNING)
logging.getLogger("httpx").propagate = False

logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO,
datefmt='%Y-%m-%d %H:%M:%S'
)

if __name__ == '__main__':
settings = Settings()
settings = Settings()


class StartWithBotMention(filters.MessageFilter):
def filter(self, message):
return message.text.startswith(settings.BOT_NICKNAME)


if __name__ == '__main__':
application = ApplicationBuilder().token(settings.BOT_TOKEN).build()
application.add_handler(CommandHandler('start', handler_start))
application.add_handler(CommandHandler('help', handler_help))
application.add_handler(CommandHandler('md_guide', handler_md_guide))
application.add_handler(CallbackQueryHandler(handler_button))
application.add_handler(MessageHandler(filters.Entity("mention"), handler_message))
application.add_handler(MessageHandler(filters.ATTACHMENT, handler_message))
application.add_handler(MessageHandler(filters.ALL, handler_message))
application.add_error_handler(native_error_handler)
application.add_handler(MessageHandler(StartWithBotMention(), handler_message))
application.add_handler(MessageHandler(filters.ChatType.PRIVATE, handler_message))
application.run_polling()
80 changes: 33 additions & 47 deletions src/answers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,51 +6,37 @@

@dataclass
class Answers:
issue_open = '\n> Issue open by {} via {}'
assign_change = '\n> Assign changed from {} to @{} by {}.'
issue_close = '\n> Issue closed by {}.'
issue_reopen = '\n> Issue reopened by {}.'
start = '🤖 I\'m a <a href="https://github.com/Annndruha/issue-github-telegram-bot">bot</a>' \
' for creating a GitHub issue in <a href="https://github.com/{}">your organization</a> repositories' \
' directly from this chat. \nMore info in /help'
help = 'For create an issue, mention me and enter the title of the issue.' \
'\nIf you want to provide a description, after the title break the line and write a ' \
'description. Below is an example of a message to create an issue:' \
'\n\n{} Issue title\nDescription of issue\nSee more by calling the bot command: /md_guide'
no_title = 'After the mention, you need to enter the title of the issue. More in /help'
help = 'To create an issue, mention me and after the mention enter the title of the issue.' \
'\nIf you want to provide a description, after the title of the issue, break the line and write a ' \
'description. Below is an example of a message to create an issue:\n\n' \
'{} This is issue title\n' \
'And start with this line is description' \
'\nAnother line of description.\nSee more by calling the bot command: /md_guide'
start = '🤖 I\'m <a href="https://github.com/Annndruha/issue-github-telegram-bot">bot</a>' \
' for create issue in GitHub <a href="https://github.com/{}">organization</a>' \
'\nMore info in /help'
markdown_guide_tg = '''
Native Telegram styling are converted to Markdown in GitHub:
<i>italic</i>
<b>bold</b>
<u>underline</u>
<s>strike</s>
<code>monospace_code</code>
<span class="tg-spoiler">spoiler</span> (GitHub not supported)
<a href="https://github.com">link</a>
'''
markdown_guide_md = '''
## This is Markdown syntax
### Third level header
* list_item
* list_item
* sub_list_item
- [ ] Empty checkbox
- [x] Done checkbox
$latex = \\frac{e^5}{\\pi}$
**bold**
*italic*
`inline_code_block`
| markdown_table | version |
|--------------------:|---------|
| Python | 3.11 |
| python-telegram-bot | 20.1 |
```python
# code block
print('Hello, issue bot!')
```
[md_link](github.com/annndruha/issue-github-telegram-bot)
'''
# issue_open = '\n> Issue open by {} via {}'
# assign_change = '\n> Assign changed from {} to @{} by {}.'
# issue_close = '\n> Issue closed by {}.'
# issue_reopen = '\n> Issue reopened by {}.'
markdown_guide_tg = 'Supported Telegram styling which will be properly converted to GitHub Markdown:' \
'\n\n<i>italic</i>' \
'\n<b>bold</b>' \
'\n<s>strike</s>' \
'\n<code>monospace_code</code>' \
'\n<a href="https://github.com">link</a>'
markdown_guide_md = '# Markdown syntax ' \
'\n### Third level header' \
'\n\n* list_item' \
'\n * sub_list_item' \
'\n\n- [ ] Empty checkbox' \
'\n- [x] Done checkbox' \
'\n\n$latex = \\frac{e^5}{\\pi}$' \
'\n**bold**' \
'\n*italic*' \
'\n`inline_code_block `' \
'\n\n```python' \
'\n# code block' \
'\nprint("Hello, issue bot!")' \
'\n```' \
'\n\n[link](github.com)'\
'\n![image_link](link.to/some_image.png)'
64 changes: 30 additions & 34 deletions src/github_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@
# 2023
import logging

from gql import gql, Client
from gql import Client, gql
from gql.transport.requests import RequestsHTTPTransport
from gql.transport.requests import log as requests_logger
requests_logger.setLevel(logging.WARNING)
logging.getLogger("httpx").propagate = False


class Github:
Expand All @@ -25,65 +22,64 @@ def __init__(self, settings):
self.__read_queries()

def __read_queries(self):
with open('src/graphql/get_repos.graphql') as f:
with open('src/graphql/repositories.graphql') as f:
self.q_get_repos = gql(f.read())
with open('src/graphql/get_members.graphql') as f:
with open('src/graphql/members.graphql') as f:
self.q_get_members = gql(f.read())
with open('src/graphql/add_to_scrum.graphql') as f:
with open('src/graphql/scrum.graphql') as f:
self.q_add_to_scrum = gql(f.read())
with open('src/graphql/issue_actions.graphql') as f:
with open('src/graphql/issues.graphql') as f:
self.q_issue_actions = gql(f.read())

def open_issue(self, repo_id, title, body):
params = {'repositoryId': repo_id, 'title': title, 'body': body}
return self.client.execute(self.q_issue_actions, operation_name='CreateIssue', variable_values=params)

def transfer_issue(self, repo_id, issue_id):
params = {'repositoryId': repo_id, 'issueId': issue_id}
def transfer_issue(self, new_repo_id, issue_id):
params = {'repositoryId': new_repo_id, 'issueId': issue_id}
return self.client.execute(self.q_issue_actions, operation_name='TransferIssue', variable_values=params)

def get_repos(self, page_info):
params = {'gh_query': f'org:{self.settings.GH_ORGANIZATION_NICKNAME} archived:false fork:true is:public '
f'sort:updated'}
if page_info == 'repos_start': # start page
r = self.client.execute(self.q_get_repos, operation_name='getReposInit', variable_values=params)
elif page_info.startswith('repos_after'): # next page
params['cursor'] = page_info.split('_')[2]
r = self.client.execute(self.q_get_repos, operation_name='getReposAfter', variable_values=params)
params = {'org': self.settings.GH_ORGANIZATION_NICKNAME}
if page_info == 'rps_start': # start page
r = self.client.execute(self.q_get_repos, operation_name='GetReposInit', variable_values=params)
elif page_info.startswith('rps_af'): # next page
params['cursor'] = page_info.split('_', 2)[2]
r = self.client.execute(self.q_get_repos, operation_name='GetReposAfter', variable_values=params)
else: # previous page
params['cursor'] = page_info.split('_')[2]
r = self.client.execute(self.q_get_repos, operation_name='getReposBefore', variable_values=params)
return r['repos']

def close_issue(self, issue_id, comment=''):
params = {'issueId': issue_id}
return self.client.execute(self.q_issue_actions, operation_name='CloseIssue', variable_values=params)

def reopen_issue(self, issue_id, comment=''):
params = {'issueId': issue_id}
return self.client.execute(self.q_issue_actions, operation_name='ReopenIssue', variable_values=params)
params['cursor'] = page_info.split('_', 2)[2]
r = self.client.execute(self.q_get_repos, operation_name='GetReposBefore', variable_values=params)
return r['organization']['repositories']

def get_members(self, page_info):
params = {'org': self.settings.GH_ORGANIZATION_NICKNAME}
if page_info == 'members_start': # start page
r = self.client.execute(self.q_get_members, operation_name='GetMembersInit', variable_values=params)
elif page_info.startswith('members_after'): # next page
params['cursor'] = page_info.split('_')[2]
params['cursor'] = page_info.split('_', 2)[2]
r = self.client.execute(self.q_get_members, operation_name='GetMembersAfter', variable_values=params)
else: # previous page
params['cursor'] = page_info.split('_')[2]
params['cursor'] = page_info.split('_', 2)[2]
r = self.client.execute(self.q_get_members, operation_name='GetMembersBefore', variable_values=params)
return r['organization']['membersWithRole']

def set_assignee(self, issue_id, assign_to_id):
params = {'issueId': issue_id, 'assigneeIds': [assign_to_id]}
def close_issue(self, issue_id):
params = {'issueId': issue_id}
return self.client.execute(self.q_issue_actions, operation_name='CloseIssue', variable_values=params)

def reopen_issue(self, issue_id):
params = {'issueId': issue_id}
return self.client.execute(self.q_issue_actions, operation_name='ReopenIssue', variable_values=params)

def set_assignee(self, issue_id, member_id):
params = {'issueId': issue_id, 'assigneeIds': [member_id]}
return self.client.execute(self.q_issue_actions, operation_name='SetIssueAssign', variable_values=params)

def add_to_scrum(self, node_id):
try:
params = {'projectId': self.settings.GH_SCRUM_ID,
'contentId': node_id}
r = self.client.execute(self.q_add_to_scrum, operation_name='addToScrum', variable_values=params)
r = self.client.execute(self.q_add_to_scrum, operation_name='AddToScrum', variable_values=params)

item_id = r['addProjectV2ItemById']['item']['id']
logging.info(f'Node {node_id} successfully added to scrum with contentId= {item_id}')
Expand All @@ -92,7 +88,7 @@ def add_to_scrum(self, node_id):
'itemId': item_id,
'fieldId': self.settings.GH_SCRUM_FIELD_ID,
'value': self.settings.GH_SCRUM_FIELD_DEFAULT_STATE} # backlog column
r = self.client.execute(self.q_add_to_scrum, operation_name='setScrumStatus', variable_values=params)
r = self.client.execute(self.q_add_to_scrum, operation_name='SetScrumStatus', variable_values=params)
if 'errors' in r:
logging.warning(f'''itemId={item_id} not set status. Reason: {r['errors']}''')
except Exception as err:
Expand Down
62 changes: 0 additions & 62 deletions src/graphql/get_repos.graphql

This file was deleted.

2 changes: 1 addition & 1 deletion src/graphql/graphql.config.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file used by PyCharm GraphQL plugin
# https://github.com/JetBrains/js-graphql-intellij-plugin
schema: schema.github.graphql
schema: schema.docs.graphql
extensions:
endpoints:
Default GraphQL Endpoint:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
mutation CreateIssue ($repositoryId: ID!, $title: String!, $body: String!) {
mutation CreateIssue($repositoryId: ID!, $title: String!, $body: String!) {
createIssue(
input: {
repositoryId: $repositoryId,
Expand Down Expand Up @@ -37,6 +37,7 @@ mutation ReopenIssue($issueId: ID!) {
id
url
body
title
assignees (first: 1){
edges {
node {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ query GetMembersInit($org: String!) {
totalCount
edges {
node {
login
id
login
name
}
}
}
Expand All @@ -30,8 +31,9 @@ query GetMembersAfter($org: String!, $cursor: String!) {
totalCount
edges {
node {
login
id
login
name
}
}
}
Expand All @@ -50,8 +52,9 @@ query GetMembersBefore($org: String!, $cursor: String!) {
totalCount
edges {
node {
login
id
login
name
}
}
}
Expand Down
Loading

0 comments on commit de19edc

Please sign in to comment.