Compare commits
No commits in common. "master" and "v.0.1.0" have entirely different histories.
5 changed files with 35 additions and 321 deletions
|
@ -1,37 +0,0 @@
|
||||||
workspace:
|
|
||||||
base: /maubot-osc
|
|
||||||
path: src
|
|
||||||
|
|
||||||
pipeline:
|
|
||||||
build:
|
|
||||||
image: dock.mau.dev/maubot/maubot:latest
|
|
||||||
commands:
|
|
||||||
- mbc build /maubot-osc/src -o /maubot-osc/src/
|
|
||||||
secrets: [mbc_username, mbc_password]
|
|
||||||
release:
|
|
||||||
image: plugins/gitea-release
|
|
||||||
settings:
|
|
||||||
base_url: https://git.dennogumi.org
|
|
||||||
api_key:
|
|
||||||
from_secret: gitea_token
|
|
||||||
files:
|
|
||||||
- /maubot-osc/src/*.mbp
|
|
||||||
checksum:
|
|
||||||
- sha256
|
|
||||||
when:
|
|
||||||
event: tag
|
|
||||||
notify:
|
|
||||||
image: plugins/matrix
|
|
||||||
settings:
|
|
||||||
homeserver: https://conference.heavensinferno.net
|
|
||||||
roomid:
|
|
||||||
from_secret: roomid
|
|
||||||
accesstoken:
|
|
||||||
from_secret: access_token
|
|
||||||
userid:
|
|
||||||
from_secret: user_id
|
|
||||||
when:
|
|
||||||
status:
|
|
||||||
- failure
|
|
||||||
- success
|
|
||||||
|
|
26
README.md
26
README.md
|
@ -6,8 +6,6 @@ SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
## maubot-osc
|
## maubot-osc
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
`maubot-osc` is a simple [maubot](https://maubot.xyz/) plugin to query an [Open Build Service](https://openbuildservice.org/) instance. Basically it replicates a few of the features of the `osc` command line tool used to interact with the OBS.
|
`maubot-osc` is a simple [maubot](https://maubot.xyz/) plugin to query an [Open Build Service](https://openbuildservice.org/) instance. Basically it replicates a few of the features of the `osc` command line tool used to interact with the OBS.
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
@ -23,7 +21,6 @@ These are in addition to what maubot requires by itself.
|
||||||
|
|
||||||
- lxml
|
- lxml
|
||||||
- jinja2
|
- jinja2
|
||||||
- [cryptocode](https://github.com/gdavid7/cryptocode)
|
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
|
@ -33,11 +30,9 @@ Then you need to modify a few configuration parameters for your instance:
|
||||||
- `api_url`: the URL to make API requests to the OBS instance. By default it points to the [public OBS instance of the openSUSE project](https://build.opensuse.org) (`https://api.opensuse.org`).
|
- `api_url`: the URL to make API requests to the OBS instance. By default it points to the [public OBS instance of the openSUSE project](https://build.opensuse.org) (`https://api.opensuse.org`).
|
||||||
- `instance_url`: the main URL of the Build Service instance. Defaults to `https://build.opensuse.org`.
|
- `instance_url`: the main URL of the Build Service instance. Defaults to `https://build.opensuse.org`.
|
||||||
- `username`: A valid username for the instance
|
- `username`: A valid username for the instance
|
||||||
- `password`: A password for the username. Suggestions on how to improve this are welcome.
|
- `password`: A password for the username. **Currently kept in cleartext**. Suggestions on how to improve this are welcome.
|
||||||
- `trigger_token`: An OBS access token with the `runservice` capability. See "Creating tokens" below.
|
- `trigger_token`: An OBS access token with the `runservice` capability. See "Creating tokens" below.
|
||||||
- `rebuild_token`: An OBS access token with the `rebuild` capability. See "Creating tokens" below.
|
- `rebuild_token`: An OBS access token with the `rebuild` capability. See "Creating tokens" below.
|
||||||
- `secret`: A random string used to encrypt the password in the configuration file.
|
|
||||||
- `acl`: User IDs (`@user:homeserver.com`) which are allowed to interact with the bot. See "Access control lists" below.
|
|
||||||
|
|
||||||
### Creating tokens
|
### Creating tokens
|
||||||
|
|
||||||
|
@ -63,26 +58,9 @@ Save both tokens in the configuration. Should you need to, you can view them lat
|
||||||
|
|
||||||
Make sure your bot instance is in a room (refer to the maubot docs for how) and then type `!osc help` for help.
|
Make sure your bot instance is in a room (refer to the maubot docs for how) and then type `!osc help` for help.
|
||||||
|
|
||||||
### Access control lists (ACLs)
|
|
||||||
|
|
||||||
By default, no user is allowed to perform any operation. You need to change the `acl` section of the configuration to add user IDs (`@user:homeserver.com`) allowed to interact with the bot. Users in the `admin` list have full powers over the bot, while users in the `user` list can only perform read-only (e.g. status querying) actions.
|
|
||||||
|
|
||||||
This example shows how it works in practice:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
acl:
|
|
||||||
admin:
|
|
||||||
# Fred and Sue will be able to perform all commands
|
|
||||||
- @fred:myhome.com
|
|
||||||
- @sue:otherserver.com
|
|
||||||
user:
|
|
||||||
# Phil will only be able to run read-only (status, etc.) commands
|
|
||||||
- @phil:elsewhere.com
|
|
||||||
```
|
|
||||||
|
|
||||||
### Notice
|
### Notice
|
||||||
|
|
||||||
I made this for my own use. Don't expect high quality code or a lot of polish.
|
I made this in about three or four hours for my own use. Don't expect high quality code or a lot of polish.
|
||||||
|
|
||||||
### License
|
### License
|
||||||
|
|
||||||
|
|
|
@ -6,36 +6,9 @@ api_url: https://api.opensuse.org
|
||||||
instance_url: https://build.opensuse.org
|
instance_url: https://build.opensuse.org
|
||||||
# Build Service username
|
# Build Service username
|
||||||
username: changeme
|
username: changeme
|
||||||
# Used to encrypt the password. Set it to a random string
|
# Build Service password
|
||||||
secret: changeme
|
|
||||||
# Build Service password - will be encrypted on first run
|
|
||||||
password: changeme
|
password: changeme
|
||||||
# Build service access token with the "rebuild" capability
|
# Build service access token with the "rebuild" capability
|
||||||
rebuild_token: changeme
|
rebuild_token: changeme
|
||||||
# Build service access token with the "runservices" capability
|
# Build service access token with the "runservices" capability
|
||||||
trigger_token: changeme
|
trigger_token: changeme
|
||||||
# Aliases for repositories
|
|
||||||
repo_aliases:
|
|
||||||
kua_failed:
|
|
||||||
project: "KDE:Unstable:Applications"
|
|
||||||
package: "all"
|
|
||||||
repository: "KDE_Unstable_Frameworks_openSUSE_Factory"
|
|
||||||
state: "failed"
|
|
||||||
arch: "all"
|
|
||||||
kuf_failed:
|
|
||||||
project: "KDE:Unstable:Frameworks"
|
|
||||||
package: "all"
|
|
||||||
repository: "openSUSE_Factory"
|
|
||||||
state: "failed"
|
|
||||||
arch: "all"
|
|
||||||
kue_failed:
|
|
||||||
project: "KDE:Unstable:Extra"
|
|
||||||
package: "all"
|
|
||||||
repository: "KDE_Unstable_Frameworks_openSUSE_Factory"
|
|
||||||
state: "failed"
|
|
||||||
arch: "all"
|
|
||||||
acl:
|
|
||||||
admin:
|
|
||||||
- "@CHANGE_ME"
|
|
||||||
user: []
|
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# SPDX-License-Identifier: CC0-1.0
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
maubot: 0.1.0
|
maubot: 0.1.0
|
||||||
id: org.dennogumi.osc
|
id: org.dennogumi.osc
|
||||||
version: 0.4.0
|
version: 0.1.0
|
||||||
license: AGPL-3.0-or-later
|
license: AGPL-3.0-or-later
|
||||||
modules:
|
modules:
|
||||||
- oscbot
|
- oscbot
|
||||||
|
@ -12,6 +12,5 @@ extra_files:
|
||||||
dependencies:
|
dependencies:
|
||||||
- lxml
|
- lxml
|
||||||
- jinja2
|
- jinja2
|
||||||
- cryptocode
|
|
||||||
database: false
|
database: false
|
||||||
config: true
|
config: true
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
# SPDX-FileCopyrightText: 2022 Luca Beltrame <lbeltrame@kde.org>
|
# SPDX-FileCopyrightText: 2022 Luca Beltrame <lbeltrame@kde.org>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass
|
||||||
from time import sleep
|
|
||||||
from typing import Optional, List, Type, Tuple
|
from typing import Optional, List, Type, Tuple
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import cryptocode
|
|
||||||
import more_itertools as mit
|
|
||||||
from lxml import objectify
|
from lxml import objectify
|
||||||
from jinja2 import BaseLoader, Environment
|
from jinja2 import BaseLoader, Environment
|
||||||
|
|
||||||
|
@ -24,11 +21,13 @@ Only showing packages with {{ state }} state.
|
||||||
|
|
||||||
{% endif %}"""
|
{% endif %}"""
|
||||||
|
|
||||||
STATUS_TEMPLATE = """
|
REPO_TEMPLATE = """
|
||||||
{% for package in packages %}
|
#### {{ repo.name }} - {{ repo.arch }}
|
||||||
|
|
||||||
|
{% for package in repo.packages %}
|
||||||
{%if package.status != "disabled" %}
|
{%if package.status != "disabled" %}
|
||||||
{% set build_log_url = "{0}/package/live_build_log/{1}/{2}/{3}/{4}/".format(
|
{% set build_log_url = "{0}/public/build/{1}/{2}/{3}/{4}/_log".format(
|
||||||
base_url, project, package.name, repo.name, repo.arch) %}
|
base_url,project, repo.name, repo.arch, package.name) %}
|
||||||
- {{ package.name }} - *[{{ package.status }}]({{ build_log_url }})*
|
- {{ package.name }} - *[{{ package.status }}]({{ build_log_url }})*
|
||||||
{% else %}
|
{% else %}
|
||||||
- {{ package.name }} - *{{ package.status }}*
|
- {{ package.name }} - *{{ package.status }}*
|
||||||
|
@ -36,21 +35,6 @@ STATUS_TEMPLATE = """
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ALIAS_TEMPLATE = """
|
|
||||||
### Configured aliases
|
|
||||||
|
|
||||||
{% for alias, contents in repo_aliases.items() %}
|
|
||||||
#### {{ alias }}
|
|
||||||
|
|
||||||
- project: {{ contents.project }}
|
|
||||||
- repository: {{ contents.repository }}
|
|
||||||
- package(s): {{ contents.package }}
|
|
||||||
- architecture(s): {{ contents.arch }}
|
|
||||||
- state: {{ contents.state }}
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class BuildResult:
|
class BuildResult:
|
||||||
|
@ -62,7 +46,7 @@ class BuildResult:
|
||||||
class BuildRepository:
|
class BuildRepository:
|
||||||
name: str
|
name: str
|
||||||
arch: str
|
arch: str
|
||||||
packages: List[BuildResult] = field(default_factory=list)
|
packages: List[BuildResult]
|
||||||
|
|
||||||
|
|
||||||
class Config(BaseProxyConfig):
|
class Config(BaseProxyConfig):
|
||||||
|
@ -72,18 +56,8 @@ class Config(BaseProxyConfig):
|
||||||
helper.copy("instance_url")
|
helper.copy("instance_url")
|
||||||
helper.copy("rebuild_token")
|
helper.copy("rebuild_token")
|
||||||
helper.copy("trigger_token")
|
helper.copy("trigger_token")
|
||||||
helper.copy("secret")
|
|
||||||
|
|
||||||
password = self["password"]
|
|
||||||
if len(password) < 91 and not password.endswith("=="):
|
|
||||||
encrypted_password = cryptocode.encrypt(password, self["secret"])
|
|
||||||
helper.base["password"] = encrypted_password
|
|
||||||
else:
|
|
||||||
helper.copy("password")
|
|
||||||
|
|
||||||
helper.copy("username")
|
helper.copy("username")
|
||||||
helper.copy("repo_aliases")
|
helper.copy("password")
|
||||||
helper.copy("acl")
|
|
||||||
|
|
||||||
|
|
||||||
class OSCBot(Plugin):
|
class OSCBot(Plugin):
|
||||||
|
@ -102,31 +76,6 @@ class OSCBot(Plugin):
|
||||||
def get_config_class(cls) -> Type[BaseProxyConfig]:
|
def get_config_class(cls) -> Type[BaseProxyConfig]:
|
||||||
return Config
|
return Config
|
||||||
|
|
||||||
def get_alias(self, project_alias: str) -> Tuple[str, str, str, str, str]:
|
|
||||||
data = self.config["repo_aliases"][project_alias]
|
|
||||||
# There is no concept of non-positional arguments in maubot
|
|
||||||
# So we just use "all" in case we want to skip something
|
|
||||||
package = data["package"] if data["package"] != "all" else None
|
|
||||||
repository = (data["repository"] if data["repository"] != "all"
|
|
||||||
else None)
|
|
||||||
arch = data["arch"] if data["arch"] != "all" else None
|
|
||||||
project = data["project"]
|
|
||||||
state = data["state"] if data["state"] != "all" else None
|
|
||||||
|
|
||||||
return (project, package, repository, state, arch)
|
|
||||||
|
|
||||||
def check_acl(self, user_id: str, need_admin: bool = False) -> bool:
|
|
||||||
|
|
||||||
acls = self.config["acl"]
|
|
||||||
if not need_admin:
|
|
||||||
if user_id in acls["admin"] or acls["user"]:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
if user_id in acls["admin"]:
|
|
||||||
return True
|
|
||||||
self.log.debug(f"Denied operation as {user_id}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
async def parse_rebuilpac(
|
async def parse_rebuilpac(
|
||||||
self,
|
self,
|
||||||
project: str,
|
project: str,
|
||||||
|
@ -163,8 +112,7 @@ class OSCBot(Plugin):
|
||||||
arch: Optional[str] = None) -> List[BuildRepository]:
|
arch: Optional[str] = None) -> List[BuildRepository]:
|
||||||
|
|
||||||
username = self.config["username"]
|
username = self.config["username"]
|
||||||
password = cryptocode.decrypt(self.config["password"],
|
password = self.config["password"]
|
||||||
self.config["secret"])
|
|
||||||
|
|
||||||
api_url = self.config["api_url"]
|
api_url = self.config["api_url"]
|
||||||
api_call = f"{api_url}/build/{project}/_result"
|
api_call = f"{api_url}/build/{project}/_result"
|
||||||
|
@ -228,124 +176,10 @@ class OSCBot(Plugin):
|
||||||
async def osc(self) -> None:
|
async def osc(self) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@osc.subcommand("alias", help="Manage aliases")
|
|
||||||
async def alias(self) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
@alias.subcommand("list", help="List configured aliases")
|
|
||||||
async def list_aliases(self, evt: MessageEvent) -> None:
|
|
||||||
|
|
||||||
if not self.check_acl(evt.sender, need_admin=False):
|
|
||||||
await evt.reply("You are not authorized to perform this action.")
|
|
||||||
return
|
|
||||||
|
|
||||||
body = self.template.from_string(ALIAS_TEMPLATE)
|
|
||||||
|
|
||||||
if self.config.get("repo_aliases", None) is None:
|
|
||||||
await evt.reply("No aliases defined.")
|
|
||||||
return
|
|
||||||
|
|
||||||
message = body.render(repo_aliases=self.config["repo_aliases"])
|
|
||||||
await evt.respond(message, markdown=True)
|
|
||||||
|
|
||||||
@alias.subcommand("edit", help="Edit configured aliases")
|
|
||||||
@command.argument("alias", "alias name")
|
|
||||||
@command.argument("project", "project name")
|
|
||||||
@command.argument("package", "package name", required=False)
|
|
||||||
@command.argument("repository", "repository", required=False)
|
|
||||||
@command.argument("arch", "architecture", required=False)
|
|
||||||
@command.argument("state", "state", required=False)
|
|
||||||
async def edit_alias(self, evt: MessageEvent,
|
|
||||||
alias: str,
|
|
||||||
project: Optional[str] = None,
|
|
||||||
package: Optional[str] = None,
|
|
||||||
repository: Optional[str] = None,
|
|
||||||
arch: Optional[str] = None,
|
|
||||||
state: Optional[str] = None) -> None:
|
|
||||||
|
|
||||||
if not self.check_acl(evt.sender, need_admin=True):
|
|
||||||
await evt.reply("You are not authorized to perform this action.")
|
|
||||||
return
|
|
||||||
|
|
||||||
if alias not in self.config["repo_aliases"]:
|
|
||||||
await evt.respond(f"Unknown alias {alias}")
|
|
||||||
return
|
|
||||||
|
|
||||||
alias_data = self.config["repo_aliases"][alias]
|
|
||||||
|
|
||||||
if project:
|
|
||||||
alias_data["project"] = project
|
|
||||||
|
|
||||||
if repository:
|
|
||||||
alias_data["repository"] = repository
|
|
||||||
|
|
||||||
if package:
|
|
||||||
alias_data["package"] = package
|
|
||||||
|
|
||||||
if arch:
|
|
||||||
alias_data["arch"] = alias
|
|
||||||
|
|
||||||
if state:
|
|
||||||
alias_data["state"] = state
|
|
||||||
|
|
||||||
self.config["repo_aliases"][alias] = alias_data
|
|
||||||
self.config.save()
|
|
||||||
await evt.reply(f"Alias {alias} successfully changed.")
|
|
||||||
|
|
||||||
@alias.subcommand("create", help="Create a new alias")
|
|
||||||
@command.argument("alias", "alias name")
|
|
||||||
@command.argument("project", "project name")
|
|
||||||
@command.argument("package", "package name", required=False)
|
|
||||||
@command.argument("repository", "repository", required=False)
|
|
||||||
@command.argument("arch", "architecture", required=False)
|
|
||||||
@command.argument("state", "state", required=False)
|
|
||||||
async def create_alias(self, evt: MessageEvent,
|
|
||||||
alias: str,
|
|
||||||
project: Optional[str] = None,
|
|
||||||
package: Optional[str] = None,
|
|
||||||
repository: Optional[str] = None,
|
|
||||||
arch: Optional[str] = None,
|
|
||||||
state: Optional[str] = None) -> None:
|
|
||||||
|
|
||||||
if not self.check_acl(evt.sender, need_admin=True):
|
|
||||||
await evt.reply("You are not authorized to perform this action.")
|
|
||||||
return
|
|
||||||
|
|
||||||
repository = "all" if not repository else repository
|
|
||||||
package = "all" if not package else package
|
|
||||||
arch = "all" if not arch else arch
|
|
||||||
state = "all" if not state else state
|
|
||||||
alias_data = {
|
|
||||||
"project": project,
|
|
||||||
"package": package,
|
|
||||||
"repository": repository,
|
|
||||||
"arch": arch,
|
|
||||||
"state": state
|
|
||||||
}
|
|
||||||
self.config["repo_aliases"][alias] = alias_data
|
|
||||||
self.config.save()
|
|
||||||
await evt.reply(f"Alias {alias} successfully created.")
|
|
||||||
|
|
||||||
@alias.subcommand("delete", help="Delete an alias", aliases=("rm", ))
|
|
||||||
@command.argument("alias", "alias name")
|
|
||||||
async def delete_alias(self, evt: MessageEvent, alias: str):
|
|
||||||
|
|
||||||
if not self.check_acl(evt.sender, need_admin=True):
|
|
||||||
await evt.reply("You are not authorized to perform this action.")
|
|
||||||
return
|
|
||||||
|
|
||||||
if alias not in self.config["repo_aliases"]:
|
|
||||||
await evt.respond(f"Unknown alias {alias}")
|
|
||||||
return
|
|
||||||
|
|
||||||
del self.config["repo_aliases"][alias]
|
|
||||||
self.config.save()
|
|
||||||
await evt.reply(f"Alias {alias} successfully deleted.")
|
|
||||||
|
|
||||||
@osc.subcommand(
|
@osc.subcommand(
|
||||||
"rebuildpac", aliases=("rb",),
|
"rebuildpac", aliases=("rb",),
|
||||||
help="Rebuild a package or all packages in the repositories")
|
help="Rebuild a package or all packages in the repositories")
|
||||||
@command.argument("project", "project name/alias")
|
@command.argument("project", "project name")
|
||||||
@command.argument("package", "package name (or \"all\" for all packages)")
|
@command.argument("package", "package name (or \"all\" for all packages)")
|
||||||
@command.argument("repository", "repository (optional)", required=False)
|
@command.argument("repository", "repository (optional)", required=False)
|
||||||
@command.argument("arch", "architecture (optional)", required=False)
|
@command.argument("arch", "architecture (optional)", required=False)
|
||||||
|
@ -355,18 +189,12 @@ class OSCBot(Plugin):
|
||||||
repository: Optional[str] = None,
|
repository: Optional[str] = None,
|
||||||
arch: Optional[str] = None) -> None:
|
arch: Optional[str] = None) -> None:
|
||||||
|
|
||||||
if not self.check_acl(evt.sender, need_admin=True):
|
if package == "all":
|
||||||
await evt.reply("You are not authorized to perform this action.")
|
package = None
|
||||||
return
|
if repository == "all":
|
||||||
|
repository = None
|
||||||
if project in self.config["repo_aliases"]:
|
if arch == "all":
|
||||||
# We're not interested in state and we query package explicitly
|
arch = None
|
||||||
project, _, repository, _, arch = self.get_alias(project)
|
|
||||||
else:
|
|
||||||
repository = None if repository == "all" else repository
|
|
||||||
arch = None if arch == "all" else arch
|
|
||||||
|
|
||||||
package = None if package == "all" else package
|
|
||||||
|
|
||||||
result, status = await self.parse_rebuilpac(project, package,
|
result, status = await self.parse_rebuilpac(project, package,
|
||||||
repository,
|
repository,
|
||||||
|
@ -387,10 +215,6 @@ class OSCBot(Plugin):
|
||||||
project: str,
|
project: str,
|
||||||
package: str) -> None:
|
package: str) -> None:
|
||||||
|
|
||||||
if not self.check_acl(evt.sender, need_admin=True):
|
|
||||||
await evt.reply("You are not authorized to perform this action.")
|
|
||||||
return
|
|
||||||
|
|
||||||
token = self.config["trigger_token"]
|
token = self.config["trigger_token"]
|
||||||
trigger_url = f"{self.config['api_url']}/trigger/runservice"
|
trigger_url = f"{self.config['api_url']}/trigger/runservice"
|
||||||
params = {"project": project, "package": package}
|
params = {"project": project, "package": package}
|
||||||
|
@ -420,18 +244,16 @@ class OSCBot(Plugin):
|
||||||
repository: Optional[str] = None,
|
repository: Optional[str] = None,
|
||||||
arch: Optional[str] = None) -> None:
|
arch: Optional[str] = None) -> None:
|
||||||
|
|
||||||
if not self.check_acl(evt.sender, need_admin=False):
|
|
||||||
await evt.reply("You are not authorized to perform this action.")
|
|
||||||
return
|
|
||||||
|
|
||||||
if project in self.config["repo_aliases"]:
|
|
||||||
project, package, repository, state, arch = self.get_alias(project)
|
|
||||||
else:
|
|
||||||
# There is no concept of non-positional arguments in maubot
|
# There is no concept of non-positional arguments in maubot
|
||||||
# So we just use "all" in case we want to skip something
|
# So we just use "all" in case we want to skip something
|
||||||
package = None if package == "all" else package
|
if state == "all":
|
||||||
repository = None if repository == "all" else repository
|
state = None
|
||||||
arch = None if arch == "all" else arch
|
if package == "all":
|
||||||
|
package = None
|
||||||
|
if repository == "all":
|
||||||
|
repository = None
|
||||||
|
if arch == "all":
|
||||||
|
arch = None
|
||||||
|
|
||||||
response = await self.parse_status(project, package, state=state,
|
response = await self.parse_status(project, package, state=state,
|
||||||
repo=repository, arch=arch)
|
repo=repository, arch=arch)
|
||||||
|
@ -446,29 +268,8 @@ class OSCBot(Plugin):
|
||||||
|
|
||||||
base_url = self.config["instance_url"]
|
base_url = self.config["instance_url"]
|
||||||
|
|
||||||
if len(response) > 50:
|
|
||||||
await evt.respond(f"Too many repositories ({len(response)})"
|
|
||||||
f"to display in project {project}, "
|
|
||||||
"please narrow down your search.")
|
|
||||||
return
|
|
||||||
|
|
||||||
for repository in response:
|
for repository in response:
|
||||||
await evt.respond(f"#### {repository.name} - {repository.arch}",
|
body = self.template.from_string(REPO_TEMPLATE)
|
||||||
markdown=True)
|
message = body.render(repo=repository, project=project,
|
||||||
|
base_url=base_url)
|
||||||
# Just so that someone doesn't flood by listing the whole
|
|
||||||
# openSUSE:Factory
|
|
||||||
if len(repository.packages) > 1000:
|
|
||||||
await evt.respond(
|
|
||||||
f"Too many packages ({len(repository.packages)}) "
|
|
||||||
"to display, please narrow down your search.")
|
|
||||||
continue
|
|
||||||
# To avoid creating too large messages, we chunk packages in
|
|
||||||
# groups of 100
|
|
||||||
for packagelist in mit.chunked(repository.packages, 100):
|
|
||||||
body = self.template.from_string(STATUS_TEMPLATE)
|
|
||||||
message = body.render(packages=packagelist, base_url=base_url,
|
|
||||||
project=project, repo=repository)
|
|
||||||
await evt.respond(message, markdown=True)
|
await evt.respond(message, markdown=True)
|
||||||
# Wait 200 milliseconds to avoid triggering rate limiting
|
|
||||||
sleep(0.2)
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue