Allow setting an avatar image for the bot's account
This commit is contained in:
27
Cargo.lock
generated
27
Cargo.lock
generated
@@ -371,6 +371,9 @@ dependencies = [
|
||||
"http",
|
||||
"log",
|
||||
"matrix-sdk",
|
||||
"mime",
|
||||
"mime-sniffer",
|
||||
"mime_guess2",
|
||||
"quick-xml",
|
||||
"regex",
|
||||
"reqwest",
|
||||
@@ -1826,12 +1829,34 @@ version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "mime-sniffer"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b8b2a64cd735f1d5f17ff6701ced3cc3c54851f9448caf454cd9c923d812408"
|
||||
dependencies = [
|
||||
"mime",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime2ext"
|
||||
version = "0.1.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbf6f36070878c42c5233846cd3de24cf9016828fd47bc22957a687298bb21fc"
|
||||
|
||||
[[package]]
|
||||
name = "mime_guess2"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1706dc14a2e140dec0a7a07109d9a3d5890b81e85bd6c60b906b249a77adf0ca"
|
||||
dependencies = [
|
||||
"mime",
|
||||
"phf",
|
||||
"phf_shared",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
@@ -2034,6 +2059,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2043,6 +2069,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@@ -34,6 +34,9 @@ futures = "0.3"
|
||||
http = "1.3"
|
||||
log = { version = "0.4", features = ["std"] }
|
||||
matrix-sdk = { version = "0.13", features = ["anyhow", "markdown", "rustls-tls"], default-features = false }
|
||||
mime = "0.3"
|
||||
mime-sniffer = "0.1"
|
||||
mime_guess2 = "2"
|
||||
quick-xml = { version = "0.38", features = ["serialize"] }
|
||||
regex = "1"
|
||||
reqwest = { version = "0.12", default-features = false, features = ["charset", "http2", "gzip", "rustls-tls-native-roots", "system-proxy"] }
|
||||
|
@@ -6,6 +6,8 @@ bind_port: 3000
|
||||
user_id: "@mybebot:example.com"
|
||||
# Password for Matrix user.
|
||||
password: "secret-matrix-account-password"
|
||||
# Optional path to an image file to use as the bot account's avatar.
|
||||
avatar_image_path: "/path/to/avatar.jpg"
|
||||
# All Gitlab-specific settings are under here.
|
||||
gitlab_webhook:
|
||||
# Optional prefix to serve the webhook path under (default is empty string).
|
||||
|
@@ -87,6 +87,7 @@ pub struct Config {
|
||||
#[serde(deserialize_with = "crate::matrix::deser_user_id")]
|
||||
pub user_id: OwnedUserId,
|
||||
pub password: String,
|
||||
pub avatar_image_path: Option<PathBuf>,
|
||||
pub gitlab_webhook: Option<GitlabWebhookConfig>,
|
||||
pub mail_archive: Option<MailArchiveConfig>,
|
||||
}
|
||||
|
18
src/main.rs
18
src/main.rs
@@ -41,13 +41,25 @@ async fn run() -> anyhow::Result<()> {
|
||||
.ok_or_else(|| anyhow!("Config file should be passed as only parameter"))?;
|
||||
let mut config = config::load(config_path).await?;
|
||||
|
||||
let matrix_client = matrix::connect(&config).await.context("Failed to connect to Matrix")?;
|
||||
let mut join_handles = Vec::default();
|
||||
|
||||
let handles = if let Some(mail_archive) = config.mail_archive.take() {
|
||||
let matrix_client = matrix::connect(&config).await.context("Failed to connect to Matrix")?;
|
||||
if let Some(avatar_path) = config.avatar_image_path {
|
||||
let matrix_client = matrix_client.clone();
|
||||
let handle = tokio::spawn(async move {
|
||||
if let Err(err) = matrix::set_avatar_if_needed(&matrix_client, avatar_path).await {
|
||||
warn!("Failed to set matrix avatar: {err}");
|
||||
}
|
||||
});
|
||||
join_handles.push(handle);
|
||||
}
|
||||
|
||||
let mail_join_handles = if let Some(mail_archive) = config.mail_archive.take() {
|
||||
mail_archive::start_polling(mail_archive, matrix_client.clone())?
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
join_handles.extend(mail_join_handles);
|
||||
|
||||
if let Some(gitlab_webhook) = config.gitlab_webhook.take() {
|
||||
let gitlab = gitlab_webhook::build_route(gitlab_webhook, matrix_client.clone());
|
||||
@@ -60,7 +72,7 @@ async fn run() -> anyhow::Result<()> {
|
||||
axum::serve(listener, gitlab).await?;
|
||||
}
|
||||
|
||||
join_all(handles).await;
|
||||
join_all(join_handles).await;
|
||||
|
||||
error!("No functionality is configured; exiting");
|
||||
exit(1);
|
||||
|
@@ -14,16 +14,19 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{fmt, process::exit, time::Duration};
|
||||
use std::{fmt, path::Path, process::exit, time::Duration};
|
||||
|
||||
use futures::TryFutureExt;
|
||||
use matrix_sdk::{
|
||||
config::SyncSettings,
|
||||
media::MediaFormat,
|
||||
room::Room,
|
||||
ruma::{OwnedRoomOrAliasId, OwnedUserId, RoomOrAliasId, UserId},
|
||||
BaseRoom, Client,
|
||||
};
|
||||
use mime_sniffer::MimeTypeSnifferExt;
|
||||
use serde::de;
|
||||
use tokio::time::sleep;
|
||||
use tokio::{fs, time::sleep};
|
||||
|
||||
use crate::config::Config;
|
||||
|
||||
@@ -60,6 +63,34 @@ pub async fn connect(config: &Config) -> anyhow::Result<Client> {
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
pub async fn set_avatar_if_needed<P: AsRef<Path>>(client: &Client, avatar_path: P) -> anyhow::Result<()> {
|
||||
let new_avatar = fs::read(&avatar_path).await?;
|
||||
|
||||
let account = client.account();
|
||||
let cur_avatar = account.get_avatar(MediaFormat::File).await?;
|
||||
|
||||
if cur_avatar.as_ref() != Some(&new_avatar) {
|
||||
// Falling back to extension matching because for some reason mime-sniffer can't handle SVG
|
||||
// files, even though it seems like it should be able to.
|
||||
if let Some(media_type) = new_avatar
|
||||
.as_slice()
|
||||
.sniff_mime_type_ext()
|
||||
.or_else(|| mime_guess2::from_path(&avatar_path).first())
|
||||
{
|
||||
info!("Setting avatar image from '{}'", avatar_path.as_ref().to_string_lossy());
|
||||
account
|
||||
.upload_avatar(&media_type, new_avatar)
|
||||
.map_err(Into::into)
|
||||
.map_ok(|_| ())
|
||||
.await
|
||||
} else {
|
||||
Err(anyhow!("Cannot determine media type of avatar image"))
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn ensure_room_joined(matrix_client: &Client, room_id: &OwnedRoomOrAliasId) -> anyhow::Result<Room> {
|
||||
fn room_matches(a_room: &BaseRoom, our_room: &OwnedRoomOrAliasId) -> bool {
|
||||
let our_room_str = our_room.as_str();
|
||||
|
Reference in New Issue
Block a user