Added ability to create new issues
This commit is contained in:
parent
bed8f7ac8d
commit
1f3073476a
|
|
@ -2,16 +2,20 @@ gitea_instance: git.arcticsoftware.no
|
|||
gitea_token: ABC123ABC1ABC123ABC1ABC123ABC1ABC123ABC1ABC123ABC1ABC123ABC123A
|
||||
gitea_owner: someowner
|
||||
gitea_repo: somerepo
|
||||
gitea_bug_label: 1
|
||||
gitea_feature_label: 1
|
||||
feature_issue_room: '!someroom:matrix.server.com'
|
||||
bug_issue_room: '!someotherrroom:matrix.server.com'
|
||||
about_text: |
|
||||
Jeg er ment som en bro mellom Arctic Software sitt kodedeponi og
|
||||
matrix chatten. Du kan sende forskjellige kommandoer til meg og
|
||||
jeg vil utføre de oppgavene du forespør. Her er en liste over
|
||||
kommandoene du kan bruke:<br /><br />
|
||||
|
||||
<code>"!om"</code> eller <code>"!hjelp"</code>:<br />
|
||||
<code>!om</code> eller <code>"!hjelp"</code>:<br />
|
||||
Viser denne teksten<br /><br />
|
||||
|
||||
<code>"!saker" [status]</code>:<br />
|
||||
<code>!saker [status]</code>:<br />
|
||||
Viser en liste over aktive oppgaver i kodedeponiet. Oppgaver kan
|
||||
være buggs som må løses, ønskede fremtidige funksjoner eller andre
|
||||
forespørsler og saker. Å sende status er valgfritt og vil filtrere
|
||||
|
|
@ -19,36 +23,72 @@ about_text: |
|
|||
'lukket', eller 'alle' for å velge hva slags saker du ønsker å
|
||||
vise.<br /><br />
|
||||
|
||||
Eksempel: "!saker" eller "!saker åpen"<br /><br />
|
||||
<code>Eksempel: "!saker" eller "!saker lukket"</code><br /><br />
|
||||
|
||||
<code>"!eldre"</code>:<br />
|
||||
Viser en liste over ferdige/utgåtte oppgaver i kodedeponiet.<br /><br />
|
||||
|
||||
<code>"!ny" [type]</code>:<br />
|
||||
<code>!ny [type] "[tittel]" "[beskrivelse]"</code>:<br />
|
||||
Oppretter en ny oppgave. Følg opp kommandoen med hva slags type
|
||||
oppgave du vil opprette, dette kan være enten "bug" eller
|
||||
"forbedring".<br /><br />
|
||||
oppgave du vil opprette og en tittel og en lengre beskrivelse.
|
||||
Type kan være enten "bug" eller "forbedring". Tittelen kan maks
|
||||
være 250 tegn. Men prøv å hold tittelen så kort og konsis som
|
||||
mulig.<br /><br />
|
||||
|
||||
Etter at du har kjørt denne kommandoen, vil jeg sende deg en PM
|
||||
hvor du må fullføre beskrivelsen av oppgaven. Jeg vil så lagre
|
||||
oppgaven i kodedeponiets sakssystem, samtidig som jeg vil
|
||||
opprette en tråd på matrix serveren brukere kan delta i, så
|
||||
de kan chatte og diskutere videre om oppgaven.<br /><br />
|
||||
Jeg vil så lagre oppgaven i kodedeponiets sakssystem, samtidig
|
||||
som jeg vil opprette en tråd på matrix serveren brukere kan
|
||||
delta i, for å chatte og diskutere videre om oppgaven.<br /><br />
|
||||
|
||||
Eksempel: "!ny bug"<br /><br />
|
||||
<code>Eksempel: !ny bug "Dette er en passe kort tittel" "Lengre beskrivelse"</code><br /><br />
|
||||
|
||||
<code>"!sak" [saksnr]</code>:<br />
|
||||
<code>!sak [saksnr]</code>:<br />
|
||||
Viser detaljer om en oppgave/sak. Følg opp kommandoen med et
|
||||
saksnr.<br /><br />
|
||||
|
||||
Eksempel: "!sak 83"<br /><br />
|
||||
<code>Eksempel: "!sak 83"</code><br /><br />
|
||||
|
||||
Denne botten er lagd/kodet av Helge-Mikael Nordgård og
|
||||
eventuelle spørsmål om bruk/utvikling av den kan rettes til
|
||||
meg <a href="mailto:surface-fancy-deem@duck.com">per mail</a>.
|
||||
meg <a href="mailto:surface-fancy-deem@duck.com">per mail</a>.<br /><br />
|
||||
|
||||
Koden for denne botten er lisensiert under AGPL-3.0 og er fritt
|
||||
tilgjengelig på mitt private
|
||||
<a href="https://git.outlands.no/heno/devops-bot">git deponi</a>.
|
||||
issue_bug_text: |
|
||||
Ny 🪲 rapport fra {user} ({userId}):<br /><br />
|
||||
|
||||
<strong>{title}</strong> <em>(Saksnr 🚩 {casenr} 🚩)</em>
|
||||
<hr />
|
||||
{description}
|
||||
<hr />
|
||||
Saken på kodedeponiet kan leses 🔗 <a href="{link}">her</a>.
|
||||
issue_feature_text: |
|
||||
Ny 🎫 forespørsel fra {user} ({userId}):<br /><br />
|
||||
|
||||
<strong>{title}</strong> <em>(Saksnr 🚩 {casenr} 🚩)</em>
|
||||
<hr />
|
||||
{description}
|
||||
<hr />
|
||||
Saken på kodedeponiet kan leses 🔗 <a href="{link}">her</a>.
|
||||
issue_disclaimer_text: |
|
||||
Denne saken ble videresendt fra dev bot fra [matrise serveren](https://chat.zuul.no)
|
||||
|
||||
Brukeren ({nick}) som opprettet saken, kan kontaktes der ({userId})
|
||||
command_new_help: |
|
||||
**"!Ny" Kommando hjelp:**
|
||||
|
||||
Skriv ny [type] "[tittel]" "[Beskrivelse]" for å opprette en ny sak.
|
||||
|
||||
**Eksempel:**
|
||||
```!ny bug "Jeg fant en bug" "Dette er en lengre beskrivelse av buggen"```
|
||||
|
||||
Husk gåseøyne rundt tittel og beskrivelsen så jeg kan klart tyde og skille
|
||||
de tre argumentene du trenger å oppgi for å opprette en ny sak. (Ikke bruk
|
||||
gåseøyne for type sak du oppretter, bare tittel og beskrivelse).
|
||||
|
||||
{error}
|
||||
command_pm_error: |
|
||||
Beklager, men du må kjøre kommandoen fra et rom hvor jeg befinner meg utenom personlige meldinger. Vennligst prøv igjen
|
||||
command_pm_help: |
|
||||
Har sendt deg en PM med informasjon om meg selv og hvordan du sender kommandoer til meg
|
||||
command_pm_success: |
|
||||
Har sendt deg en PM med resultatet fra forespørselen din
|
||||
Har sendt deg en PM med resultatet fra forespørselen din
|
||||
command_success: |
|
||||
Forespørselen din ble utført. {result}
|
||||
167
devops_bot.py
167
devops_bot.py
|
|
@ -4,6 +4,7 @@ import json
|
|||
import time
|
||||
import requests
|
||||
import asyncio
|
||||
import shlex
|
||||
import aiohttp
|
||||
|
||||
from mautrix.client import Client, InternalEventType, MembershipEventDispatcher, SyncStream
|
||||
|
|
@ -20,10 +21,19 @@ class Config(BaseProxyConfig):
|
|||
helper.copy("gitea_token")
|
||||
helper.copy("gitea_owner")
|
||||
helper.copy("gitea_repo")
|
||||
helper.copy("gitea_bug_label")
|
||||
helper.copy("gitea_feature_label")
|
||||
helper.copy("feature_issue_room")
|
||||
helper.copy("bug_issue_room")
|
||||
helper.copy("about_text")
|
||||
helper.copy("issue_bug_text")
|
||||
helper.copy("issue_feature_text")
|
||||
helper.copy("issue_disclaimer_text")
|
||||
helper.copy("command_pm_error")
|
||||
helper.copy("command_pm_help")
|
||||
helper.copy("command_pm_success")
|
||||
helper.copy("command_new_help")
|
||||
helper.copy("command_success")
|
||||
|
||||
class DevopsBot(Plugin):
|
||||
async def start(self) -> None:
|
||||
|
|
@ -44,8 +54,29 @@ class DevopsBot(Plugin):
|
|||
def g_repo(self) -> str:
|
||||
return self.config["gitea_repo"]
|
||||
|
||||
def g_bug_label(self) -> int:
|
||||
return self.config["gitea_bug_label"]
|
||||
|
||||
def g_feature_label(self) -> int:
|
||||
return self.config["gitea_feature_label"]
|
||||
|
||||
def g_bug_room(self) -> str:
|
||||
return self.config["bug_issue_room"]
|
||||
|
||||
def g_feature_room(self) -> str:
|
||||
return self.config["feature_issue_room"]
|
||||
|
||||
def g_about(self) -> str:
|
||||
return self.config["about_text"]
|
||||
|
||||
def g_bug_text(self) -> str:
|
||||
return self.config["issue_bug_text"]
|
||||
|
||||
def g_feature_text(self) -> str:
|
||||
return self.config["issue_feature_text"]
|
||||
|
||||
def g_disclaimer(self) -> str:
|
||||
return self.config["issue_disclaimer_text"]
|
||||
|
||||
def g_c_self_error(self) -> str:
|
||||
return self.config["command_pm_error"]
|
||||
|
|
@ -55,6 +86,12 @@ class DevopsBot(Plugin):
|
|||
|
||||
def g_c_self_success(self) -> str:
|
||||
return self.config["command_pm_success"]
|
||||
|
||||
def g_c_new_help(self) -> str:
|
||||
return self.config["command_new_help"]
|
||||
|
||||
def g_c_command_success(self) -> str:
|
||||
return self.config["command_success"]
|
||||
|
||||
# Match all the registered pm rooms in pm_rooms with
|
||||
# the username and return the room id
|
||||
|
|
@ -72,7 +109,7 @@ class DevopsBot(Plugin):
|
|||
if state not in ['open', 'closed', 'all']:
|
||||
raise ValueError("State må være enten 'open', 'closed', eller 'all")
|
||||
|
||||
params = {'state': state}
|
||||
params = {'state': state, 'limit': 500}
|
||||
query_string = urlencode(params)
|
||||
url = f"https://{self.g_instance()}/api/v1/repos/{self.g_owner()}/{self.g_repo()}/issues?{query_string}"
|
||||
headers = {
|
||||
|
|
@ -84,9 +121,117 @@ class DevopsBot(Plugin):
|
|||
async with session.get(url, headers=headers) as response:
|
||||
response.raise_for_status()
|
||||
return await response.json()
|
||||
|
||||
# Post an issue to the configured repository
|
||||
async def post_issue(self, label: int, title: str, body: str) -> list:
|
||||
url = f"https://{self.g_instance()}/api/v1/repos/{self.g_owner()}/{self.g_repo()}/issues"
|
||||
headers = {
|
||||
"Authorization": f"token {self.g_token()}",
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
issue_data = {
|
||||
"title": title,
|
||||
"body": body,
|
||||
"labels": [
|
||||
label
|
||||
]
|
||||
}
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(url, headers=headers, json=issue_data) as response:
|
||||
response.raise_for_status()
|
||||
return await response.json()
|
||||
|
||||
@command.new(name="ny", aliases=["new"])
|
||||
@command.argument("args", pass_raw=True, required=False)
|
||||
async def ny(self, evt: MessageEvent, args: str = None) -> None:
|
||||
# We want to see the commands ran in a open room, so
|
||||
# we can get a sense of it's usage
|
||||
if evt.room_id in self.pm_rooms:
|
||||
await evt.reply(self.g_c_self_error())
|
||||
return
|
||||
|
||||
# Check correct formatting of the arguments first
|
||||
|
||||
try:
|
||||
arg_list = shlex.split(args)
|
||||
except ValueError:
|
||||
await evt.reply(self.g_c_new_help().format(error="Feil i formatering: Forsikre deg om at du bruker gåseøyne separat for tittel og beskrivelse"))
|
||||
return
|
||||
|
||||
if len(arg_list) < 3:
|
||||
await evt.reply(self.g_c_new_help().format(error="Feil i formatering. Ikke nok argumenter. Bruk !ny [type] \"[Tittel]\" \"[Beskrivelse]\""))
|
||||
return
|
||||
if len(arg_list) > 3:
|
||||
await evt.reply(self.g_c_new_help().format(error="Feil i formatering. For mange argumenter. Bruk !ny [type] \"[Tittel]\" \"[Beskrivelse]\""))
|
||||
return
|
||||
|
||||
issue_type, title, description = arg_list
|
||||
|
||||
if not title.strip() or not description.strip():
|
||||
await evt.reply(self.g_c_new_help().format(error="Feil i formatering. Tittel og/eller beskrivelse kan ikke være tomme verdier"))
|
||||
return
|
||||
|
||||
if len(title) > 250:
|
||||
await evt.reply(self.g_c_new_help().format(error="Feil i formatering. Tittelen kan ikke overskride 250 tegn."))
|
||||
return
|
||||
|
||||
if issue_type == 'bug':
|
||||
label = self.g_bug_label()
|
||||
elif issue_type == 'forbedring':
|
||||
label = self.g_feature_label()
|
||||
else:
|
||||
await evt.reply(f"Noe gikk feil med parsingen av argumentet 'type' (Mottok '{issue_type}' forventet 'bug' eller 'forbedring')")
|
||||
return
|
||||
|
||||
# Append information about the sender to the description text before posting it
|
||||
description_without_signature = description # store description in a new variable for use on matrix
|
||||
|
||||
description += f"\n\n--\n"
|
||||
description += self.g_disclaimer().format(
|
||||
nick=self.client.parse_user_id(evt.sender)[0],
|
||||
userId=evt.sender
|
||||
)
|
||||
|
||||
# Create issue on the git repository, and make a thread on this matrix server
|
||||
|
||||
try:
|
||||
new_issue = await self.post_issue(label=label, title=title, body=description)
|
||||
if issue_type == 'bug':
|
||||
await self.client.send_notice(self.g_bug_room(), html=self.g_bug_text().format(
|
||||
user=self.client.parse_user_id(evt.sender)[0],
|
||||
userId=evt.sender,
|
||||
title=title,
|
||||
description=description_without_signature,
|
||||
link=new_issue["html_url"],
|
||||
casenr=new_issue["number"]
|
||||
))
|
||||
else:
|
||||
await self.client.send_notice(self.g_feature_room(), html=self.g_feature_text().format(
|
||||
user=self.client.parse_user_id(evt.sender)[0],
|
||||
userId=evt.sender,
|
||||
title=title,
|
||||
description=description_without_signature,
|
||||
link=new_issue["html_url"],
|
||||
casenr=new_issue["number"]
|
||||
))
|
||||
|
||||
await evt.reply(self.g_c_command_success().format(
|
||||
result=f"Sak med saksnr {new_issue['number']} ble opprettet. Bruk !sak for å se detaljer"
|
||||
))
|
||||
except aiohttp.ClientError as e:
|
||||
await evt.reply(f"Feil ved forespørsel: {e}")
|
||||
except aiohttp.ContentTypeError as e:
|
||||
await evt.reply(f"Feil ved syntaktisk analyse av JSON forespørsel: {e}")
|
||||
except KeyError as e:
|
||||
await evt.reply(f"En feil oppstod ved forsøk på å hente saksdata fra ny sak: {e}")
|
||||
except Exception as e:
|
||||
await evt.reply(f"En uventet feil oppstod: {e}")
|
||||
|
||||
@command.new(name="saker", aliases=["cases"])
|
||||
@command.argument("status", pass_raw=True, required=False)
|
||||
@command.argument("status", required=False)
|
||||
async def saker(self, evt: MessageEvent, status: str) -> None:
|
||||
# We want to see the commands ran in a open room, so
|
||||
# we can get a sense of it's usage
|
||||
|
|
@ -108,16 +253,14 @@ class DevopsBot(Plugin):
|
|||
await evt.reply(f"Ukjent status {status}. Status kan enten være 'åpen', 'lukket', eller 'alle'")
|
||||
return
|
||||
|
||||
output = """
|
||||
|Saksnr | Tittel | Type(r) |
|
||||
| :--- | :----: | ---: |
|
||||
"""
|
||||
output = "<table><tr><th>Saksnr</th><th>Tittel</th><th>Type(r)</th></tr>"
|
||||
|
||||
for issue in issues:
|
||||
output += f"\n| {issue['number']} | {issue['title']} | "
|
||||
for label in issue['labels']:
|
||||
output += f"{label['name']} "
|
||||
output += '|'
|
||||
output += f"<tr><td>{issue['number']}</td><td>{issue['title']}</td><td>"
|
||||
labels = " ".join(label['name'] for label in issue['labels'])
|
||||
output += f"{labels}</td></tr>"
|
||||
|
||||
output += "</table>"
|
||||
|
||||
# Check if the bot already has a PM session with the user
|
||||
# so we don't stack up invites to new rooms for each
|
||||
|
|
@ -125,11 +268,11 @@ class DevopsBot(Plugin):
|
|||
pm_room = await self.has_pm_room(evt.sender)
|
||||
|
||||
if pm_room is not 'NONE':
|
||||
await self.client.send_text(pm_room, output)
|
||||
await self.client.send_text(pm_room, html=output)
|
||||
else:
|
||||
pm_room = await self.client.create_room(is_direct=True, invitees=[evt.sender])
|
||||
self.pm_rooms.add(pm_room)
|
||||
await self.client.send_text(pm_room, output)
|
||||
await self.client.send_text(pm_room, html=output)
|
||||
|
||||
await evt.reply(self.g_c_self_success())
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
maubot: 0.1.0
|
||||
id: no.arcticsoftware.devopsbot
|
||||
version: 0.1.23
|
||||
version: 0.1.34
|
||||
license: AGPL-3.0-or-later
|
||||
modules:
|
||||
- devops_bot
|
||||
|
|
|
|||
BIN
no.arcticsoftware.devopsbot-v0.1.34.mbp
Normal file
BIN
no.arcticsoftware.devopsbot-v0.1.34.mbp
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user