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` 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
 | 
			
		||||
| 
						 | 
				
			
			@ -23,7 +21,6 @@ These are in addition to what maubot requires by itself.
 | 
			
		|||
 | 
			
		||||
- lxml
 | 
			
		||||
- jinja2
 | 
			
		||||
- [cryptocode](https://github.com/gdavid7/cryptocode)
 | 
			
		||||
 | 
			
		||||
### 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`).
 | 
			
		||||
- `instance_url`: the main URL of the Build Service instance. Defaults to `https://build.opensuse.org`.
 | 
			
		||||
- `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.
 | 
			
		||||
- `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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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.
 | 
			
		||||
 | 
			
		||||
### 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
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,36 +6,9 @@ api_url: https://api.opensuse.org
 | 
			
		|||
instance_url: https://build.opensuse.org
 | 
			
		||||
# Build Service username
 | 
			
		||||
username: changeme
 | 
			
		||||
# Used to encrypt the password. Set it to a random string
 | 
			
		||||
secret: changeme
 | 
			
		||||
# Build Service password - will be encrypted on first run
 | 
			
		||||
# Build Service password
 | 
			
		||||
password: changeme
 | 
			
		||||
# Build service access token with the "rebuild" capability
 | 
			
		||||
rebuild_token: changeme
 | 
			
		||||
# Build service access token with the "runservices" capability
 | 
			
		||||
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
 | 
			
		||||
maubot: 0.1.0
 | 
			
		||||
id: org.dennogumi.osc
 | 
			
		||||
version: 0.4.0
 | 
			
		||||
version: 0.1.0
 | 
			
		||||
license: AGPL-3.0-or-later
 | 
			
		||||
modules:
 | 
			
		||||
  - oscbot
 | 
			
		||||
| 
						 | 
				
			
			@ -12,6 +12,5 @@ extra_files:
 | 
			
		|||
dependencies:
 | 
			
		||||
  - lxml
 | 
			
		||||
  - jinja2
 | 
			
		||||
  - cryptocode
 | 
			
		||||
database: false
 | 
			
		||||
config: true
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,13 +1,10 @@
 | 
			
		|||
# SPDX-FileCopyrightText: 2022 Luca Beltrame <lbeltrame@kde.org>
 | 
			
		||||
# SPDX-License-Identifier: AGPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
from dataclasses import dataclass, field
 | 
			
		||||
from time import sleep
 | 
			
		||||
from dataclasses import dataclass
 | 
			
		||||
from typing import Optional, List, Type, Tuple
 | 
			
		||||
 | 
			
		||||
import aiohttp
 | 
			
		||||
import cryptocode
 | 
			
		||||
import more_itertools as mit
 | 
			
		||||
from lxml import objectify
 | 
			
		||||
from jinja2 import BaseLoader, Environment
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -24,11 +21,13 @@ Only showing packages with {{ state }} state.
 | 
			
		|||
 | 
			
		||||
{% endif %}"""
 | 
			
		||||
 | 
			
		||||
STATUS_TEMPLATE = """
 | 
			
		||||
{% for package in packages %}
 | 
			
		||||
REPO_TEMPLATE = """
 | 
			
		||||
#### {{ repo.name }} - {{ repo.arch }}
 | 
			
		||||
 | 
			
		||||
{% for package in repo.packages %}
 | 
			
		||||
{%if package.status != "disabled" %}
 | 
			
		||||
{% set build_log_url = "{0}/package/live_build_log/{1}/{2}/{3}/{4}/".format(
 | 
			
		||||
    base_url, project, package.name, repo.name, repo.arch) %}
 | 
			
		||||
{% set build_log_url = "{0}/public/build/{1}/{2}/{3}/{4}/_log".format(
 | 
			
		||||
    base_url,project, repo.name, repo.arch, package.name) %}
 | 
			
		||||
- {{ package.name }} - *[{{ package.status }}]({{ build_log_url }})*
 | 
			
		||||
{% else %}
 | 
			
		||||
- {{ package.name }} - *{{ package.status }}*
 | 
			
		||||
| 
						 | 
				
			
			@ -36,21 +35,6 @@ STATUS_TEMPLATE = """
 | 
			
		|||
{% 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
 | 
			
		||||
class BuildResult:
 | 
			
		||||
| 
						 | 
				
			
			@ -62,7 +46,7 @@ class BuildResult:
 | 
			
		|||
class BuildRepository:
 | 
			
		||||
    name: str
 | 
			
		||||
    arch: str
 | 
			
		||||
    packages: List[BuildResult] = field(default_factory=list)
 | 
			
		||||
    packages: List[BuildResult]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Config(BaseProxyConfig):
 | 
			
		||||
| 
						 | 
				
			
			@ -72,18 +56,8 @@ class Config(BaseProxyConfig):
 | 
			
		|||
        helper.copy("instance_url")
 | 
			
		||||
        helper.copy("rebuild_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("repo_aliases")
 | 
			
		||||
        helper.copy("acl")
 | 
			
		||||
        helper.copy("password")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OSCBot(Plugin):
 | 
			
		||||
| 
						 | 
				
			
			@ -102,31 +76,6 @@ class OSCBot(Plugin):
 | 
			
		|||
    def get_config_class(cls) -> Type[BaseProxyConfig]:
 | 
			
		||||
        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(
 | 
			
		||||
            self,
 | 
			
		||||
            project: str,
 | 
			
		||||
| 
						 | 
				
			
			@ -163,8 +112,7 @@ class OSCBot(Plugin):
 | 
			
		|||
            arch: Optional[str] = None) -> List[BuildRepository]:
 | 
			
		||||
 | 
			
		||||
        username = self.config["username"]
 | 
			
		||||
        password = cryptocode.decrypt(self.config["password"],
 | 
			
		||||
                                      self.config["secret"])
 | 
			
		||||
        password = self.config["password"]
 | 
			
		||||
 | 
			
		||||
        api_url = self.config["api_url"]
 | 
			
		||||
        api_call = f"{api_url}/build/{project}/_result"
 | 
			
		||||
| 
						 | 
				
			
			@ -228,124 +176,10 @@ class OSCBot(Plugin):
 | 
			
		|||
    async def osc(self) -> None:
 | 
			
		||||
        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(
 | 
			
		||||
        "rebuildpac", aliases=("rb",),
 | 
			
		||||
        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("repository", "repository (optional)", required=False)
 | 
			
		||||
    @command.argument("arch", "architecture (optional)", required=False)
 | 
			
		||||
| 
						 | 
				
			
			@ -355,18 +189,12 @@ class OSCBot(Plugin):
 | 
			
		|||
                         repository: Optional[str] = None,
 | 
			
		||||
                         arch: 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 project in self.config["repo_aliases"]:
 | 
			
		||||
            # We're not interested in state and we query package explicitly
 | 
			
		||||
            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
 | 
			
		||||
        if package == "all":
 | 
			
		||||
            package = None
 | 
			
		||||
        if repository == "all":
 | 
			
		||||
            repository = None
 | 
			
		||||
        if arch == "all":
 | 
			
		||||
            arch = None
 | 
			
		||||
 | 
			
		||||
        result, status = await self.parse_rebuilpac(project, package,
 | 
			
		||||
                                                    repository,
 | 
			
		||||
| 
						 | 
				
			
			@ -387,10 +215,6 @@ class OSCBot(Plugin):
 | 
			
		|||
                         project: str,
 | 
			
		||||
                         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"]
 | 
			
		||||
        trigger_url = f"{self.config['api_url']}/trigger/runservice"
 | 
			
		||||
        params = {"project": project, "package": package}
 | 
			
		||||
| 
						 | 
				
			
			@ -420,18 +244,16 @@ class OSCBot(Plugin):
 | 
			
		|||
                     repository: Optional[str] = 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
 | 
			
		||||
        # So we just use "all" in case we want to skip something
 | 
			
		||||
            package = None if package == "all" else package
 | 
			
		||||
            repository = None if repository == "all" else repository
 | 
			
		||||
            arch = None if arch == "all" else arch
 | 
			
		||||
        if state == "all":
 | 
			
		||||
            state = None
 | 
			
		||||
        if package == "all":
 | 
			
		||||
            package = None
 | 
			
		||||
        if repository == "all":
 | 
			
		||||
            repository = None
 | 
			
		||||
        if arch == "all":
 | 
			
		||||
            arch = None
 | 
			
		||||
 | 
			
		||||
        response = await self.parse_status(project, package, state=state,
 | 
			
		||||
                                           repo=repository, arch=arch)
 | 
			
		||||
| 
						 | 
				
			
			@ -446,29 +268,8 @@ class OSCBot(Plugin):
 | 
			
		|||
 | 
			
		||||
        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:
 | 
			
		||||
            await evt.respond(f"#### {repository.name} - {repository.arch}",
 | 
			
		||||
                              markdown=True)
 | 
			
		||||
 | 
			
		||||
            # 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)
 | 
			
		||||
            body = self.template.from_string(REPO_TEMPLATE)
 | 
			
		||||
            message = body.render(repo=repository, project=project,
 | 
			
		||||
                                  base_url=base_url)
 | 
			
		||||
            await evt.respond(message, markdown=True)
 | 
			
		||||
                # Wait 200 milliseconds to avoid triggering rate limiting
 | 
			
		||||
                sleep(0.2)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue