mirror of
https://github.com/matrix-construct/construct
synced 2024-05-29 08:13:46 +02:00
modules/web_hook: Add control panel on the status bar.
This commit is contained in:
parent
d2c47b0d9e
commit
219571e69c
|
@ -509,76 +509,156 @@ github_handle__dependabot_alert(std::ostream &out,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the message resulting from the push and react with the status.
|
static size_t
|
||||||
static ircd::m::event::id::buf
|
clear_reactions(const m::room &room,
|
||||||
github_find_push_reaction_id(const m::room &room,
|
const m::user::id &user_id,
|
||||||
const m::user::id &user_id,
|
const m::event::id &event_id)
|
||||||
const m::event::id &push_event_id,
|
|
||||||
const string_view &label)
|
|
||||||
{
|
{
|
||||||
const auto type_match
|
const m::relates relations
|
||||||
{
|
{
|
||||||
[](const string_view &type) noexcept
|
index(event_id)
|
||||||
{
|
|
||||||
return type == "m.reaction";
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto user_match
|
const auto user_match
|
||||||
{
|
{
|
||||||
[&user_id](const string_view &sender) noexcept
|
[&user_id](const auto &sender)
|
||||||
{
|
{
|
||||||
return sender && sender == user_id;
|
return sender == user_id;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto content_match
|
size_t ret(0);
|
||||||
|
relations.for_each("m.annotation", [&]
|
||||||
|
(const auto &ref_idx, const json::object &content, const m::relates_to &relates)
|
||||||
{
|
{
|
||||||
[&push_event_id, &label](const json::object &content)
|
if(!m::query(ref_idx, "sender", user_match))
|
||||||
{
|
return true;
|
||||||
const json::object relates_to
|
|
||||||
{
|
|
||||||
content["m.relates_to"]
|
|
||||||
};
|
|
||||||
|
|
||||||
const json::string event_id
|
if(m::redacted(ref_idx))
|
||||||
{
|
return true;
|
||||||
relates_to["event_id"]
|
|
||||||
};
|
|
||||||
|
|
||||||
const json::string key
|
const auto ref_id(m::event_id(ref_idx));
|
||||||
{
|
m::redact(room, user_id, ref_id, "cleared");
|
||||||
relates_to["key"]
|
++ret;
|
||||||
};
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
return event_id == push_event_id && endswith(key, label);
|
return ret;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
template<class closure>
|
||||||
// Limit the search to a maximum of recent messages from the
|
static ircd::m::event::id::buf
|
||||||
// webhook user and total messages so we don't run out of control
|
_find_reaction_id(const m::room &room,
|
||||||
// and scan the whole room history.
|
const m::user::id &user_id,
|
||||||
int lim[2] { 768, 384 };
|
const m::event::id &event_id,
|
||||||
m::room::events it{room};
|
closure&& func)
|
||||||
for(; it && lim[0] > 0 && lim[1] > 0; --it, --lim[0])
|
{
|
||||||
{
|
const m::relates relations
|
||||||
if(!m::query(std::nothrow, it.event_idx(), "sender", user_match))
|
{
|
||||||
continue;
|
index(event_id)
|
||||||
|
};
|
||||||
--lim[1];
|
|
||||||
if(!m::query(std::nothrow, it.event_idx(), "type", type_match))
|
const auto user_match
|
||||||
continue;
|
{
|
||||||
|
[&user_id](const auto &sender)
|
||||||
if(!m::query(std::nothrow, it.event_idx(), "content", content_match))
|
{
|
||||||
continue;
|
return sender == user_id;
|
||||||
|
}
|
||||||
return m::event_id(std::nothrow, it.event_idx());
|
};
|
||||||
}
|
|
||||||
|
m::event::id::buf ret;
|
||||||
return {};
|
relations.for_each("m.annotation", [&func, &user_match, &ret]
|
||||||
|
(const auto &ref_idx, const json::object &content, const m::relates_to &relates)
|
||||||
|
{
|
||||||
|
if(!m::query(ref_idx, "sender", user_match))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if(m::redacted(ref_idx))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if(func(relates.source))
|
||||||
|
{
|
||||||
|
ret = m::event_id(ref_idx);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ircd::m::event::id::buf
|
||||||
|
find_reaction_id(const m::room &room,
|
||||||
|
const m::user::id &user_id,
|
||||||
|
const m::event::id &event_id,
|
||||||
|
const string_view &label)
|
||||||
|
{
|
||||||
|
return _find_reaction_id(room, user_id, event_id, [&label]
|
||||||
|
(const auto &relates)
|
||||||
|
{
|
||||||
|
const json::string key
|
||||||
|
{
|
||||||
|
relates["key"]
|
||||||
|
};
|
||||||
|
|
||||||
|
return key == label;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static ircd::m::event::id::buf
|
||||||
|
find_reaction_id_endswith(const m::room &room,
|
||||||
|
const m::user::id &user_id,
|
||||||
|
const m::event::id &event_id,
|
||||||
|
const string_view &label)
|
||||||
|
{
|
||||||
|
return _find_reaction_id(room, user_id, event_id, [&]
|
||||||
|
(const auto &relates)
|
||||||
|
{
|
||||||
|
const json::string key
|
||||||
|
{
|
||||||
|
relates["key"]
|
||||||
|
};
|
||||||
|
|
||||||
|
return endswith(key, label);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
clear_reaction(const m::room &room,
|
||||||
|
const m::user::id &user_id,
|
||||||
|
const m::event::id &event_id,
|
||||||
|
const string_view &label)
|
||||||
|
{
|
||||||
|
const auto reaction_id
|
||||||
|
{
|
||||||
|
find_reaction_id(room, user_id, event_id, label)
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!reaction_id)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m::redact(room, user_id, reaction_id, "cleared");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
clear_reaction_endswith(const m::room &room,
|
||||||
|
const m::user::id &user_id,
|
||||||
|
const m::event::id &event_id,
|
||||||
|
const string_view &label)
|
||||||
|
{
|
||||||
|
const auto reaction_id
|
||||||
|
{
|
||||||
|
find_reaction_id_endswith(room, user_id, event_id, label)
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!reaction_id)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m::redact(room, user_id, reaction_id, "cleared");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the message resulting from the push and react with the status.
|
|
||||||
static ircd::m::event::id::buf
|
static ircd::m::event::id::buf
|
||||||
github_find_job_table(const m::room &room,
|
github_find_job_table(const m::room &room,
|
||||||
const m::user::id &user_id,
|
const m::user::id &user_id,
|
||||||
|
@ -736,6 +816,53 @@ github_request(unique_const_buffer &out,
|
||||||
return github_request(content, out, method, repo, fmt, std::forward<args>(a)...);
|
return github_request(content, out, method, repo, fmt, std::forward<args>(a)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
github_run_for_each_jobs(const string_view &repo,
|
||||||
|
const string_view &run_id,
|
||||||
|
const function_bool<json::object> &closure)
|
||||||
|
{
|
||||||
|
unique_const_buffer buf;
|
||||||
|
const json::object response
|
||||||
|
{
|
||||||
|
github_request(buf, "GET", repo, "actions/runs/%s/jobs", run_id)
|
||||||
|
};
|
||||||
|
|
||||||
|
const json::array jobs
|
||||||
|
{
|
||||||
|
response["jobs"]
|
||||||
|
};
|
||||||
|
|
||||||
|
for(const json::object job : jobs)
|
||||||
|
if(!closure(job))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
github_run_cancel(const string_view &repo,
|
||||||
|
const string_view &run_id)
|
||||||
|
{
|
||||||
|
unique_const_buffer buf;
|
||||||
|
github_request(buf, "POST", repo, "actions/runs/%s/cancel", run_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
github_run_rerun(const string_view &repo,
|
||||||
|
const string_view &run_id)
|
||||||
|
{
|
||||||
|
unique_const_buffer buf;
|
||||||
|
github_request(buf, "POST", repo, "actions/runs/%s/rerun", run_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
github_run_rerun_failed(const string_view &repo,
|
||||||
|
const string_view &run_id)
|
||||||
|
{
|
||||||
|
unique_const_buffer buf;
|
||||||
|
github_request(buf, "POST", repo, "actions/runs/%s/rerun-failed-jobs", run_id);
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
github_handle__workflow_run(std::ostream &out,
|
github_handle__workflow_run(std::ostream &out,
|
||||||
const json::object &content)
|
const json::object &content)
|
||||||
|
@ -755,6 +882,7 @@ github_handle__workflow_run(std::ostream &out,
|
||||||
created_at{workflow_run["created_at"]},
|
created_at{workflow_run["created_at"]},
|
||||||
updated_at{workflow_run["updated_at"]},
|
updated_at{workflow_run["updated_at"]},
|
||||||
run_started_at{workflow_run["run_started_at"]},
|
run_started_at{workflow_run["run_started_at"]},
|
||||||
|
attempt{workflow_run["run_attempt"]},
|
||||||
run_id{workflow_run["id"]};
|
run_id{workflow_run["id"]};
|
||||||
|
|
||||||
const auto _webhook_room_id
|
const auto _webhook_room_id
|
||||||
|
@ -803,7 +931,7 @@ github_handle__workflow_run(std::ostream &out,
|
||||||
const auto reaction_id
|
const auto reaction_id
|
||||||
{
|
{
|
||||||
push_event_id && action != "requested"? // skip search on first action
|
push_event_id && action != "requested"? // skip search on first action
|
||||||
github_find_push_reaction_id(_webhook_room, _webhook_user, push_event_id, name):
|
find_reaction_id_endswith(_webhook_room, _webhook_user, push_event_id, name):
|
||||||
m::event::id::buf{}
|
m::event::id::buf{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -812,6 +940,35 @@ github_handle__workflow_run(std::ostream &out,
|
||||||
|
|
||||||
m::annotate(_webhook_room, _webhook_user, push_event_id, annote);
|
m::annotate(_webhook_room, _webhook_user, push_event_id, annote);
|
||||||
|
|
||||||
|
if(status == "completed")
|
||||||
|
{
|
||||||
|
const fmt::bsprintf<128> alt
|
||||||
|
{
|
||||||
|
"job status table %s %s %s",
|
||||||
|
github_repopath(content),
|
||||||
|
run_id,
|
||||||
|
attempt,
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto job_table_id
|
||||||
|
{
|
||||||
|
github_find_job_table(_webhook_room, _webhook_user, alt)
|
||||||
|
};
|
||||||
|
|
||||||
|
if(job_table_id)
|
||||||
|
switch(hash(conclusion))
|
||||||
|
{
|
||||||
|
case "success"_:
|
||||||
|
case "skipped"_:
|
||||||
|
clear_reactions(_webhook_room, _webhook_user, job_table_id);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
clear_reaction(_webhook_room, _webhook_user, job_table_id, "⭕"_sv);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool outputs{false};
|
bool outputs{false};
|
||||||
if(action == "requested" && conclusion == "failure")
|
if(action == "requested" && conclusion == "failure")
|
||||||
{
|
{
|
||||||
|
@ -857,6 +1014,7 @@ github_handle__workflow_job(std::ostream &out,
|
||||||
head_sha{workflow_job["head_sha"]},
|
head_sha{workflow_job["head_sha"]},
|
||||||
started_at{workflow_job["started_at"]},
|
started_at{workflow_job["started_at"]},
|
||||||
completed_at{workflow_job["completed_at"]},
|
completed_at{workflow_job["completed_at"]},
|
||||||
|
attempt{workflow_job["run_attempt"]},
|
||||||
run_id{workflow_job["run_id"]},
|
run_id{workflow_job["run_id"]},
|
||||||
job_id{workflow_job["id"]};
|
job_id{workflow_job["id"]};
|
||||||
|
|
||||||
|
@ -898,14 +1056,20 @@ github_handle__workflow_job(std::ostream &out,
|
||||||
default: annote = "❓️"_sv; break;
|
default: annote = "❓️"_sv; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fmt::bsprintf<96> alt
|
const fmt::bsprintf<128> alt
|
||||||
{
|
{
|
||||||
"job %s status table", run_id
|
"job status table %s %s %s",
|
||||||
|
github_repopath(content),
|
||||||
|
run_id,
|
||||||
|
attempt,
|
||||||
};
|
};
|
||||||
|
|
||||||
const fmt::bsprintf<96> alt_up
|
const fmt::bsprintf<128> alt_up
|
||||||
{
|
{
|
||||||
"job %s status update", run_id
|
"job status update %s %s %s",
|
||||||
|
github_repopath(content),
|
||||||
|
run_id,
|
||||||
|
attempt,
|
||||||
};
|
};
|
||||||
|
|
||||||
// slow this bird down
|
// slow this bird down
|
||||||
|
@ -1013,7 +1177,6 @@ github_handle__workflow_job(std::ostream &out,
|
||||||
}}
|
}}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
char headbuf[512] {0};
|
char headbuf[512] {0};
|
||||||
std::stringstream heading;
|
std::stringstream heading;
|
||||||
pubsetbuf(heading, headbuf);
|
pubsetbuf(heading, headbuf);
|
||||||
|
@ -1032,7 +1195,36 @@ github_handle__workflow_job(std::ostream &out,
|
||||||
|
|
||||||
tab = ircd::strlcat(buf, "</td></tr></table>");
|
tab = ircd::strlcat(buf, "</td></tr></table>");
|
||||||
|
|
||||||
m::msghtml(_webhook_room, _webhook_user, tab, alt);
|
const auto table_event_id
|
||||||
|
{
|
||||||
|
m::msghtml(_webhook_room, _webhook_user, tab, alt)
|
||||||
|
};
|
||||||
|
|
||||||
|
if(table_event_id)
|
||||||
|
{
|
||||||
|
m::annotate(_webhook_room, _webhook_user, table_event_id, "⭕"_sv);
|
||||||
|
m::annotate(_webhook_room, _webhook_user, table_event_id, "🔄"_sv);
|
||||||
|
m::annotate(_webhook_room, _webhook_user, table_event_id, "↪️"_sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lex_cast<uint>(attempt) > 1)
|
||||||
|
{
|
||||||
|
const fmt::bsprintf<128> prior_alt
|
||||||
|
{
|
||||||
|
"job status table %s %s %u",
|
||||||
|
github_repopath(content),
|
||||||
|
run_id,
|
||||||
|
lex_cast<uint>(attempt) - 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto prior_table_id
|
||||||
|
{
|
||||||
|
github_find_job_table(_webhook_room, _webhook_user, prior_alt)
|
||||||
|
};
|
||||||
|
|
||||||
|
if(prior_table_id)
|
||||||
|
clear_reactions(_webhook_room, _webhook_user, prior_table_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool outputs{false};
|
bool outputs{false};
|
||||||
|
@ -1064,6 +1256,111 @@ github_handle__workflow_job(std::ostream &out,
|
||||||
return outputs;
|
return outputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
github_react_handle(const m::event &event,
|
||||||
|
m::vm::eval &)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(!webhook_room)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// XXX alias?
|
||||||
|
if(json::get<"room_id"_>(event) != webhook_room)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const m::room room
|
||||||
|
{
|
||||||
|
at<"room_id"_>(event)
|
||||||
|
};
|
||||||
|
|
||||||
|
const m::room::power power
|
||||||
|
{
|
||||||
|
room
|
||||||
|
};
|
||||||
|
|
||||||
|
// XXX ???
|
||||||
|
if(power.level_user(at<"sender"_>(event)) < 50)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const json::object relates_to
|
||||||
|
{
|
||||||
|
json::get<"content"_>(event).get("m.relates_to")
|
||||||
|
};
|
||||||
|
|
||||||
|
const json::string relates_event_id
|
||||||
|
{
|
||||||
|
relates_to["event_id"]
|
||||||
|
};
|
||||||
|
|
||||||
|
const json::string key
|
||||||
|
{
|
||||||
|
relates_to["key"]
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto relates_content
|
||||||
|
{
|
||||||
|
m::get(relates_event_id, "content")
|
||||||
|
};
|
||||||
|
|
||||||
|
const json::string relates_body
|
||||||
|
{
|
||||||
|
json::object{relates_content}.get("body")
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!startswith(relates_body, "job status table "))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto suffix
|
||||||
|
{
|
||||||
|
lstrip(relates_body, "job status table ")
|
||||||
|
};
|
||||||
|
|
||||||
|
string_view token[3];
|
||||||
|
ircd::tokens(suffix, ' ', token);
|
||||||
|
const auto &[repopath, run_id, attempt] {token};
|
||||||
|
assert(repopath);
|
||||||
|
assert(run_id);
|
||||||
|
if(!repopath || !run_id)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch(hash(key))
|
||||||
|
{
|
||||||
|
case hash("⭕"_sv):
|
||||||
|
github_run_cancel(repopath, run_id);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case hash("🔄"_sv):
|
||||||
|
github_run_rerun_failed(repopath, run_id);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case hash("↪️"_sv):
|
||||||
|
github_run_rerun(repopath, run_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(const ctx::interrupted &)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch(const std::exception &e)
|
||||||
|
{
|
||||||
|
log::error
|
||||||
|
{
|
||||||
|
"github react handle hook :%s",
|
||||||
|
e.what(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static m::hookfn<m::vm::eval &>
|
||||||
|
github_react_hook
|
||||||
|
{
|
||||||
|
github_react_handle,
|
||||||
|
{
|
||||||
|
{ "_site", "vm.effect" },
|
||||||
|
{ "type", "m.reaction" },
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
github_handle__check_run(std::ostream &out,
|
github_handle__check_run(std::ostream &out,
|
||||||
const json::object &content)
|
const json::object &content)
|
||||||
|
|
Loading…
Reference in a new issue