diff --git a/aurweb/models/package_request.py b/aurweb/models/package_request.py
index 31071df4..94ff064b 100644
--- a/aurweb/models/package_request.py
+++ b/aurweb/models/package_request.py
@@ -1,7 +1,10 @@
+import base64
+import hashlib
+
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import backref, relationship
-from aurweb import schema
+from aurweb import config, schema
from aurweb.models.declarative import Base
from aurweb.models.package_base import PackageBase as _PackageBase
from aurweb.models.request_type import RequestType as _RequestType
@@ -103,3 +106,16 @@ class PackageRequest(Base):
def status_display(self) -> str:
"""Return a display string for the Status column."""
return self.STATUS_DISPLAY[self.Status]
+
+ def ml_message_id_hash(self) -> str:
+ """Return the X-Message-ID-Hash that is used in the mailing list archive."""
+ # X-Message-ID-Hash is a base32 encoded SHA1 hash
+ msgid = f"pkg-request-{str(self.ID)}@aur.archlinux.org"
+ sha1 = hashlib.sha1(msgid.encode()).digest()
+
+ return base64.b32encode(sha1).decode()
+
+ def ml_message_url(self) -> str:
+ """Return the mailing list URL for the request."""
+ url = config.get("options", "ml_thread_url") % (self.ml_message_id_hash())
+ return url
diff --git a/conf/config.defaults b/conf/config.defaults
index 6cdffe65..06e73afe 100644
--- a/conf/config.defaults
+++ b/conf/config.defaults
@@ -25,6 +25,7 @@ max_rpc_results = 5000
max_search_results = 2500
max_depends = 1000
aur_request_ml = aur-requests@lists.archlinux.org
+ml_thread_url = https://lists.archlinux.org/archives/list/aur-requests@lists.archlinux.org/thread/%s
request_idle_time = 1209600
request_archive_time = 15552000
auto_orphan_age = 15552000
diff --git a/templates/requests.html b/templates/requests.html
index 669b46b0..697fbedb 100644
--- a/templates/requests.html
+++ b/templates/requests.html
@@ -115,8 +115,11 @@
{% if result.User %}
{{ result.User.Username }}
-
+
{% endif %}
+
+ (PRQ#{{ result.ID }})
+
{% set idle_time = config_getint("options", "request_idle_time") %}
{% set time_delta = (utcnow - result.RequestTS) | int %}
diff --git a/test/test_package_request.py b/test/test_package_request.py
index a69a0617..2bbf56c2 100644
--- a/test/test_package_request.py
+++ b/test/test_package_request.py
@@ -1,7 +1,7 @@
import pytest
from sqlalchemy.exc import IntegrityError
-from aurweb import db, time
+from aurweb import config, db, time
from aurweb.models.account_type import USER_ID
from aurweb.models.package_base import PackageBase
from aurweb.models.package_request import (
@@ -190,3 +190,41 @@ def test_package_request_status_display(user: User, pkgbase: PackageBase):
pkgreq.Status = 124
with pytest.raises(KeyError):
pkgreq.status_display()
+
+
+def test_package_request_ml_message_id_hash(user: User, pkgbase: PackageBase):
+ with db.begin():
+ pkgreq = db.create(
+ PackageRequest,
+ ID=1,
+ ReqTypeID=MERGE_ID,
+ User=user,
+ PackageBase=pkgbase,
+ PackageBaseName=pkgbase.Name,
+ Comments=str(),
+ ClosureComment=str(),
+ Status=PENDING_ID,
+ )
+
+ # A hash composed with ID=1 should result in BNNNRWOFDRSQP4LVPT77FF2GUFR45KW5
+ assert pkgreq.ml_message_id_hash() == "BNNNRWOFDRSQP4LVPT77FF2GUFR45KW5"
+
+
+def test_package_request_ml_message_url(user: User, pkgbase: PackageBase):
+ with db.begin():
+ pkgreq = db.create(
+ PackageRequest,
+ ID=1,
+ ReqTypeID=MERGE_ID,
+ User=user,
+ PackageBase=pkgbase,
+ PackageBaseName=pkgbase.Name,
+ Comments=str(),
+ ClosureComment=str(),
+ Status=PENDING_ID,
+ )
+
+ assert (
+ config.get("options", "ml_thread_url") % (pkgreq.ml_message_id_hash())
+ == pkgreq.ml_message_url()
+ )