Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
0df350dae6 | |||
52a89428f3 | |||
903577aba7 | |||
a7629a650a | |||
629e13710d | |||
2cc3b87a35 | |||
fa67902c22 | |||
2032b97f7c | |||
dcf7ef34ab | |||
4c84269c03 | |||
dc41aeac14 | |||
092b29637f | |||
61febd2745 | |||
f8cc11dc34 | |||
10f22956b6 | |||
89e37e2258 | |||
3f42a08bcb | |||
b33935b5f8 | |||
406c485055 |
@ -9,7 +9,7 @@ jobs:
|
||||
name: CI
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: https://gitea.com/actions/checkout@v3
|
||||
- uses: https://github.com/actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
@ -8,7 +8,7 @@ jobs:
|
||||
name: Publish to crates.io
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: https://gitea.com/actions/checkout@v3
|
||||
- uses: https://github.com/actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
@ -19,10 +19,15 @@ jobs:
|
||||
name: Publish to Docker Hub
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: install docker
|
||||
run: |
|
||||
apt update
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y docker.io
|
||||
apt clean
|
||||
- uses: https://gitea.com/actions/checkout@v3
|
||||
- name: docker meta
|
||||
id: meta
|
||||
uses: https://github.com/docker/metadata-action@v5
|
||||
uses: https://github.com/docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
kelnos/bebot
|
||||
@ -30,13 +35,13 @@ jobs:
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
- uses: docker/setup-qemu-action@v3
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- uses: docker/login-action@v3
|
||||
- uses: https://gitea.com/docker/setup-qemu-action@v2
|
||||
- uses: https://gitea.com/docker/setup-buildx-action@v2
|
||||
- uses: https://gitea.com/docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
- uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
|
||||
- uses: https://gitea.com/docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
|
851
Cargo.lock
generated
851
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "bebot"
|
||||
version = "0.1.0"
|
||||
version = "0.2.2"
|
||||
description = "Gitlab webhook bot that publishes events to Matrix"
|
||||
license = "GPL-3.0"
|
||||
authors = [
|
||||
@ -24,15 +24,20 @@ exclude = [
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
constant_time_eq = "0.3"
|
||||
dateparser = "0.2"
|
||||
env_logger = "0.10"
|
||||
futures = "0.3"
|
||||
http = "0.2"
|
||||
log = { version = "0.4", features = ["std"] }
|
||||
matrix-sdk = { version = "0.6", features = ["anyhow", "markdown", "rustls-tls"], default-features = false }
|
||||
quick-xml = { version = "0.30", features = ["serialize"] }
|
||||
regex = "1"
|
||||
reqwest = { version = "0.11", default-features = false, features = ["tokio-rustls", "rustls-tls-native-roots", "gzip"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
serde_regex = "1"
|
||||
serde_yaml = "0.9"
|
||||
tokio = { version = "1", default-features = false, features = ["rt-multi-thread", "macros"] }
|
||||
tokio = { version = "1", default-features = false, features = ["rt-multi-thread", "macros", "time"] }
|
||||
warp = "0.3"
|
||||
|
@ -2,57 +2,84 @@
|
||||
bind_address: 127.0.0.1
|
||||
# Port the webhook listener should bind to (default is 3000).
|
||||
bind_port: 3000
|
||||
# Optional prefix to serve the webhook path under (default is empty string).
|
||||
url_prefix: "/bebot"
|
||||
# Matrix user to sign in as.
|
||||
user_id: "@mybebot:example.com"
|
||||
# Password for Matrix user.
|
||||
password: "secret-matrix-account-password"
|
||||
# Default Matrix room to publish events to.
|
||||
default_room: "#my-project-commits:example.com"
|
||||
# Default set of events to publish. If left out, all events will be published.
|
||||
default_publish_events:
|
||||
- name: push
|
||||
# Regexes of branch names. Leave out entirely for "all branches".
|
||||
branches:
|
||||
- '^main$'
|
||||
- '^xfce-.+'
|
||||
- name: tag_push
|
||||
- name: issues
|
||||
# See the Gitlab docs for a full list of actions. If left out, all actions
|
||||
# will be published.
|
||||
actions:
|
||||
- open
|
||||
- close
|
||||
- name: merge_request
|
||||
# See the Gitlab docs for a full list of actions. If left out, all actions
|
||||
# will be published.
|
||||
actions:
|
||||
- open
|
||||
- merge
|
||||
- name: pipeline
|
||||
# See the Gitlab docs for a full list of statuses. If left out, all
|
||||
# actions will be published.
|
||||
statuses:
|
||||
- failed
|
||||
# Key-value configuration for repositories.
|
||||
repo_configs:
|
||||
# Keys are the instance name / namespace / repository name
|
||||
"gitlab.example.com/myorg/my-cool-app":
|
||||
# Each repository should use a unique, randomly-generated token. Enter
|
||||
# this token in the webhook configuration's "Secret token" on Gitlab.
|
||||
token: "abcdefg12345"
|
||||
# You can override the default_room above. Leave out to use the default.
|
||||
room: "#my-cool-app-events:example.com"
|
||||
# You can override default_events above. Leave out this section to
|
||||
# use the defaults.
|
||||
publish_events:
|
||||
- name: push
|
||||
branches:
|
||||
- main
|
||||
- name: pipeline
|
||||
statuses:
|
||||
- failed
|
||||
"gitlab.example.com/myuser/some-other-less-cool-app":
|
||||
token: "kljaslkdjaklsdjalksd"
|
||||
# This repo uses the default events and room.
|
||||
# All Gitlab-specific settings are under here.
|
||||
gitlab_webhook:
|
||||
# Optional prefix to serve the webhook path under (default is empty string).
|
||||
url_prefix: "/bebot"
|
||||
# Default Matrix room to publish Gitlab events to.
|
||||
default_room: "#my-project-commits:example.com"
|
||||
# Default set of events to publish. If left out, all events will be published.
|
||||
default_publish_events:
|
||||
- name: push
|
||||
# Regexes of branch names. Leave out entirely for "all branches".
|
||||
branches:
|
||||
- '^main$'
|
||||
- '^xfce-.+'
|
||||
- name: tag_push
|
||||
- name: issues
|
||||
# See the Gitlab docs for a full list of actions. If left out, all actions
|
||||
# will be published.
|
||||
actions:
|
||||
- open
|
||||
- close
|
||||
- name: merge_request
|
||||
# See the Gitlab docs for a full list of actions. If left out, all actions
|
||||
# will be published.
|
||||
actions:
|
||||
- open
|
||||
- merge
|
||||
- name: pipeline
|
||||
# See the Gitlab docs for a full list of statuses. If left out, all
|
||||
# actions will be published.
|
||||
statuses:
|
||||
- failed
|
||||
# Key-value configuration for repositories.
|
||||
repo_configs:
|
||||
# Keys are the instance name / namespace / repository name
|
||||
"gitlab.example.com/myorg/my-cool-app":
|
||||
# Each repository should use a unique, randomly-generated token. Enter
|
||||
# this token in the webhook configuration's "Secret token" on Gitlab.
|
||||
token: "abcdefg12345"
|
||||
# You can override the default_room above. Leave out to use the default.
|
||||
room: "#my-cool-app-events:example.com"
|
||||
# You can override default_events above. Leave out this section to
|
||||
# use the defaults.
|
||||
publish_events:
|
||||
- name: push
|
||||
branches:
|
||||
- main
|
||||
- name: pipeline
|
||||
statuses:
|
||||
- failed
|
||||
"gitlab.example.com/myuser/some-other-less-cool-app":
|
||||
token: "kljaslkdjaklsdjalksd"
|
||||
# This repo uses the default events and room.
|
||||
# The mail_archive configuration section allows you to set up bebot to publish
|
||||
# messages based on RSS feeds from mail-archive.com.
|
||||
mail_archive:
|
||||
# List of rooms that will be published to by default, unless overridden by
|
||||
# a per-list config.
|
||||
default_rooms:
|
||||
- "#some-room:example.com"
|
||||
- "#some-other-room:example.com"
|
||||
# How often bebot will fetch the RSS feed to check for updates, in seconds.
|
||||
update_interval: 60
|
||||
# A directory where bebot can store state, such as the data of the last
|
||||
# entry in the RSS feed it has seen.
|
||||
state_dir: "/var/lib/bebot/mail-archive-state"
|
||||
# A list of mailing lists.
|
||||
lists:
|
||||
# This is the list name as is displayed in mail-archive.com URLS.
|
||||
- name: "my-list@example.com"
|
||||
# Disable publishing a matrix message for replies sent to the list
|
||||
# (default true). This isn't perfect, and can only guess if a message
|
||||
# is a reply based on the subject line.
|
||||
publish_on_replies: false
|
||||
# An optional list of rooms to publish to. If not specified, the
|
||||
# default_rooms setting above will be used.
|
||||
rooms:
|
||||
- "#yet-some-other-room:example.com"
|
||||
|
123
src/config.rs
123
src/config.rs
@ -14,14 +14,13 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{collections::HashMap, fmt, fs::File, io::BufReader};
|
||||
use std::{collections::HashMap, fs::File, io::BufReader, path::PathBuf};
|
||||
|
||||
use anyhow::Context;
|
||||
use matrix_sdk::ruma::{OwnedRoomOrAliasId, OwnedUserId, RoomOrAliasId, UserId};
|
||||
use matrix_sdk::ruma::{OwnedRoomOrAliasId, OwnedUserId};
|
||||
use regex::Regex;
|
||||
use serde::de;
|
||||
|
||||
use crate::event::{IssueAction, MergeRequestAction, PipelineStatus};
|
||||
use crate::gitlab_event::{IssueAction, MergeRequestAction, PipelineStatus};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(tag = "name", rename_all = "snake_case")]
|
||||
@ -47,111 +46,53 @@ pub enum PublishEvent {
|
||||
pub struct RepoConfig {
|
||||
pub token: String,
|
||||
#[serde(default)]
|
||||
#[serde(deserialize_with = "deser_optional_room_or_alias_id")]
|
||||
#[serde(deserialize_with = "crate::matrix::deser_optional_room_or_alias_id")]
|
||||
pub room: Option<OwnedRoomOrAliasId>,
|
||||
pub publish_events: Option<Vec<PublishEvent>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Config {
|
||||
pub bind_address: Option<String>,
|
||||
pub bind_port: Option<u16>,
|
||||
pub struct GitlabWebhookConfig {
|
||||
pub url_prefix: Option<String>,
|
||||
#[serde(deserialize_with = "deser_user_id")]
|
||||
pub user_id: OwnedUserId,
|
||||
pub password: String,
|
||||
#[serde(default)]
|
||||
#[serde(deserialize_with = "deser_optional_room_or_alias_id")]
|
||||
#[serde(deserialize_with = "crate::matrix::deser_optional_room_or_alias_id")]
|
||||
pub default_room: Option<OwnedRoomOrAliasId>,
|
||||
pub default_publish_events: Option<Vec<PublishEvent>>,
|
||||
pub repo_configs: HashMap<String, RepoConfig>, // key is repo url without scheme; e.g.
|
||||
// gitlab.xfce.org/xfce/xfdesktop
|
||||
}
|
||||
|
||||
fn deser_user_id<'de, D>(deserializer: D) -> Result<OwnedUserId, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
struct UserIdVisitor;
|
||||
|
||||
impl<'de> de::Visitor<'de> for UserIdVisitor {
|
||||
type Value = OwnedUserId;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a matrix user ID")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
UserId::parse(v).map_err(E::custom)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(UserIdVisitor)
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct MailListConfig {
|
||||
pub name: String,
|
||||
#[serde(default = "default_true")]
|
||||
pub publish_on_replies: bool,
|
||||
#[serde(default)]
|
||||
pub rooms: Vec<OwnedRoomOrAliasId>,
|
||||
}
|
||||
|
||||
fn deser_room_or_alias_id<'de, D>(deserializer: D) -> Result<OwnedRoomOrAliasId, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
struct RoomOrAliasIdVisitor;
|
||||
|
||||
impl<'de> de::Visitor<'de> for RoomOrAliasIdVisitor {
|
||||
type Value = OwnedRoomOrAliasId;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a matrix room ID")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
RoomOrAliasId::parse(v).map_err(E::custom)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(RoomOrAliasIdVisitor)
|
||||
#[derive(Deserialize)]
|
||||
pub struct MailArchiveConfig {
|
||||
#[serde(default)]
|
||||
pub default_rooms: Vec<OwnedRoomOrAliasId>,
|
||||
pub update_interval: u64, // seconds
|
||||
pub state_dir: PathBuf,
|
||||
pub lists: Vec<MailListConfig>,
|
||||
}
|
||||
|
||||
fn deser_optional_room_or_alias_id<'de, D>(deserializer: D) -> Result<Option<OwnedRoomOrAliasId>, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
struct OptionalRoomOrAliasIdVisitor;
|
||||
#[derive(Deserialize)]
|
||||
pub struct Config {
|
||||
pub bind_address: Option<String>,
|
||||
pub bind_port: Option<u16>,
|
||||
#[serde(deserialize_with = "crate::matrix::deser_user_id")]
|
||||
pub user_id: OwnedUserId,
|
||||
pub password: String,
|
||||
pub gitlab_webhook: Option<GitlabWebhookConfig>,
|
||||
pub mail_archive: Option<MailArchiveConfig>,
|
||||
}
|
||||
|
||||
impl<'de> de::Visitor<'de> for OptionalRoomOrAliasIdVisitor {
|
||||
type Value = Option<OwnedRoomOrAliasId>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("null or matrix room ID")
|
||||
}
|
||||
|
||||
fn visit_none<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
Ok(Some(deser_room_or_alias_id(deserializer)?))
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
RoomOrAliasId::parse(v).map(Some).map_err(E::custom)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(OptionalRoomOrAliasIdVisitor)
|
||||
fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn load_blocking(path: &String) -> anyhow::Result<Config> {
|
||||
|
@ -185,7 +185,7 @@ impl fmt::Display for IssueAction {
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct IssueObjectAttributes {
|
||||
pub id: u32,
|
||||
pub iid: u32,
|
||||
pub title: String,
|
||||
pub action: IssueAction,
|
||||
pub url: String,
|
||||
@ -406,7 +406,7 @@ impl GitlabEventExt for GitlabEvent {
|
||||
if object_attributes.action != IssueAction::Other {
|
||||
let title = format!(
|
||||
"Issue #{} **{}**: {}",
|
||||
object_attributes.id, object_attributes.action, object_attributes.title
|
||||
object_attributes.iid, object_attributes.action, object_attributes.title
|
||||
);
|
||||
vec![markdown_link(&title, &object_attributes.url)]
|
||||
} else {
|
||||
@ -587,7 +587,7 @@ mod test {
|
||||
..
|
||||
} => {
|
||||
assert_eq!(user.name, "Administrator");
|
||||
assert_eq!(object_attributes.id, 301);
|
||||
assert_eq!(object_attributes.iid, 23);
|
||||
assert_eq!(object_attributes.action, IssueAction::Open);
|
||||
}
|
||||
_ => panic!("not an issue event"),
|
142
src/gitlab_webhook.rs
Normal file
142
src/gitlab_webhook.rs
Normal file
@ -0,0 +1,142 @@
|
||||
// bebot
|
||||
// Copyright (C) 2023 Brian Tarricone <brian@tarricone.org>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use constant_time_eq::constant_time_eq;
|
||||
use http::StatusCode;
|
||||
use matrix_sdk::{
|
||||
ruma::{events::room::message::RoomMessageEventContent, OwnedRoomOrAliasId},
|
||||
Client,
|
||||
};
|
||||
use tokio::sync::mpsc;
|
||||
use warp::{filters::BoxedFilter, reply::Reply, Filter};
|
||||
|
||||
use crate::{
|
||||
config::GitlabWebhookConfig,
|
||||
gitlab_event::{parse_ref, GitlabEvent, GitlabEventExt},
|
||||
matrix,
|
||||
};
|
||||
|
||||
pub fn build_gitlab_messages(event: &GitlabEvent) -> Vec<String> {
|
||||
let project = event.project();
|
||||
let refname = event.r#ref().map(parse_ref);
|
||||
event
|
||||
.titles()
|
||||
.iter()
|
||||
.map(|title| {
|
||||
format!(
|
||||
"\\[{}\\] {}*{}* {}",
|
||||
project.path_with_namespace,
|
||||
refname.as_ref().map(|rn| format!("`{}` ", rn)).unwrap_or_default(),
|
||||
event.user(),
|
||||
title,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub async fn handle_gitlab_event(
|
||||
event: GitlabEvent,
|
||||
room_id: &OwnedRoomOrAliasId,
|
||||
matrix_client: &Client,
|
||||
) -> anyhow::Result<()> {
|
||||
let room = matrix::ensure_room_joined(matrix_client, room_id).await?;
|
||||
for msg in build_gitlab_messages(&event) {
|
||||
debug!("Sending message to {}: {}", room_id, msg);
|
||||
let msg_content = RoomMessageEventContent::text_markdown(&msg);
|
||||
room.send(msg_content, None).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn build_route(config: GitlabWebhookConfig, matrix_client: Client) -> anyhow::Result<BoxedFilter<(impl Reply,)>> {
|
||||
let (event_tx, mut event_rx) = mpsc::channel::<(GitlabEvent, OwnedRoomOrAliasId)>(100);
|
||||
tokio::spawn(async move {
|
||||
while let Some((event, room)) = event_rx.recv().await {
|
||||
if let Err(err) = handle_gitlab_event(event, &room, &matrix_client).await {
|
||||
warn!("Failed to handle payload: {}", err);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let gitlab_root_path = if let Some(url_prefix) = config.url_prefix.as_ref() {
|
||||
url_prefix.split('/').fold(warp::any().boxed(), |last, segment| {
|
||||
if segment.is_empty() {
|
||||
last
|
||||
} else {
|
||||
last.and(warp::path(segment.to_string())).boxed()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
warp::any().boxed()
|
||||
};
|
||||
|
||||
let config = Arc::new(config);
|
||||
let gitlab = gitlab_root_path
|
||||
.and(warp::path!("hooks" / "gitlab"))
|
||||
.and(warp::post())
|
||||
.and(warp::header::<String>("x-gitlab-token"))
|
||||
.and(warp::body::json())
|
||||
.then(move |token: String, event: GitlabEvent| {
|
||||
let event_tx = event_tx.clone();
|
||||
let config = Arc::clone(&config);
|
||||
|
||||
async move {
|
||||
match event {
|
||||
GitlabEvent::Other => {
|
||||
warp::reply::with_status("Unsupported Gitlab event type", StatusCode::BAD_REQUEST)
|
||||
}
|
||||
_ => {
|
||||
let project = event.project();
|
||||
let config_key = project.web_url.replace("http://", "").replace("https://", "");
|
||||
if let Some(repo_config) = config.repo_configs.get(&config_key) {
|
||||
if !constant_time_eq(token.as_bytes(), repo_config.token.as_bytes()) {
|
||||
warn!("Invalid token for repo '{}'", config_key);
|
||||
warp::reply::with_status("Invalid token", StatusCode::FORBIDDEN)
|
||||
} else {
|
||||
debug!("payload: {:?}", event);
|
||||
if let Some(room) = &repo_config.room.as_ref().or(config.default_room.as_ref()) {
|
||||
let publish_events = repo_config
|
||||
.publish_events
|
||||
.as_ref()
|
||||
.or(config.default_publish_events.as_ref());
|
||||
if publish_events.map(|ecs| event.should_publish(ecs)).unwrap_or(true) {
|
||||
if let Err(err) = event_tx.send((event, (*room).clone())).await {
|
||||
warn!("Failed to enqueue payload: {}", err);
|
||||
}
|
||||
}
|
||||
warp::reply::with_status("OK", StatusCode::OK)
|
||||
} else {
|
||||
info!("Channel not configured for repo '{}'", config_key);
|
||||
warp::reply::with_status(
|
||||
"Matrix room not configured for repo",
|
||||
StatusCode::NOT_FOUND,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
info!("Repo '{}' unconfigured", config_key);
|
||||
warp::reply::with_status("Repo not configured", StatusCode::NOT_FOUND)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.boxed();
|
||||
|
||||
Ok(gitlab)
|
||||
}
|
275
src/mail_archive.rs
Normal file
275
src/mail_archive.rs
Normal file
@ -0,0 +1,275 @@
|
||||
// bebot
|
||||
// Copyright (C) 2023 Brian Tarricone <brian@tarricone.org>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{
|
||||
fmt,
|
||||
io::{BufReader, BufWriter, ErrorKind},
|
||||
path::PathBuf,
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
use anyhow::Context;
|
||||
use chrono::{DateTime, Utc};
|
||||
use futures::{future::join_all, FutureExt};
|
||||
use matrix_sdk::{
|
||||
ruma::{events::room::message::RoomMessageEventContent, OwnedRoomOrAliasId},
|
||||
Client,
|
||||
};
|
||||
use reqwest::redirect;
|
||||
use serde::de;
|
||||
use tokio::{fs::File, task::JoinHandle, time::sleep};
|
||||
|
||||
use crate::{
|
||||
config::{MailArchiveConfig, MailListConfig},
|
||||
matrix,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Serialize, Deserialize)]
|
||||
struct ListState {
|
||||
last_pub_date: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct RssPubDate {
|
||||
#[serde(rename = "$text", deserialize_with = "deser_rfc2616")]
|
||||
value: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct RssItem {
|
||||
title: String,
|
||||
link: String,
|
||||
#[serde(rename = "pubDate")]
|
||||
pub_date: RssPubDate,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct RssChannel {
|
||||
#[serde(rename = "item")]
|
||||
items: Vec<RssItem>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct MailRss {
|
||||
channel: RssChannel,
|
||||
}
|
||||
|
||||
async fn load_list_state(state_file: &PathBuf) -> anyhow::Result<ListState> {
|
||||
match File::open(state_file).await {
|
||||
Err(err) if err.kind() == ErrorKind::NotFound => {
|
||||
// If we have no state, we probably don't want to blast out events
|
||||
// for every single item in the RSS feed, so pretend the last time
|
||||
// we published was right now.
|
||||
let list_state = ListState {
|
||||
last_pub_date: SystemTime::now().into(),
|
||||
};
|
||||
save_list_state(list_state, state_file).await?;
|
||||
Ok(list_state)
|
||||
}
|
||||
Err(err) => Err(err)?,
|
||||
Ok(f) => {
|
||||
let r = BufReader::new(f.into_std().await);
|
||||
Ok(tokio::task::spawn_blocking(move || serde_yaml::from_reader(r)).await??)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn save_list_state(list_state: ListState, state_file: &PathBuf) -> anyhow::Result<()> {
|
||||
let f = File::options()
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.create(true)
|
||||
.open(state_file)
|
||||
.await?;
|
||||
let w = BufWriter::new(f.into_std().await);
|
||||
tokio::task::spawn_blocking(move || serde_yaml::to_writer(w, &list_state)).await??;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_list(
|
||||
list: &MailListConfig,
|
||||
state_file: &PathBuf,
|
||||
http_client: &reqwest::Client,
|
||||
url: &String,
|
||||
matrix_client: &Client,
|
||||
publish_on_replies: bool,
|
||||
room_ids: &[OwnedRoomOrAliasId],
|
||||
) -> anyhow::Result<()> {
|
||||
let list_state = load_list_state(state_file).await?;
|
||||
|
||||
let rooms_f = room_ids.iter().map(|room_id| {
|
||||
matrix::ensure_room_joined(matrix_client, room_id)
|
||||
.map(move |res| res.with_context(|| format!("Failed to join Matrix room '{}'", room_id)))
|
||||
});
|
||||
let rooms = join_all(rooms_f)
|
||||
.await
|
||||
.into_iter()
|
||||
.flat_map(|room_res| match room_res {
|
||||
Err(err) => {
|
||||
warn!("{:#}", err);
|
||||
vec![]
|
||||
}
|
||||
Ok(room) => vec![room],
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if rooms.is_empty() {
|
||||
return Err(anyhow!("Failed to join all rooms for list '{}'; skipping", list.name));
|
||||
}
|
||||
|
||||
let response = http_client
|
||||
.get(url)
|
||||
.send()
|
||||
.await
|
||||
.with_context(|| format!("Failed to fetch mail RSS feed from '{}'", url))
|
||||
.and_then(|response| {
|
||||
if !response.status().is_success() {
|
||||
Err(anyhow!(
|
||||
"Failed to fetch mail RSS feed from '{}': server returned status {}",
|
||||
url,
|
||||
response.status().as_u16()
|
||||
))
|
||||
} else {
|
||||
Ok(response)
|
||||
}
|
||||
})?;
|
||||
let body = response
|
||||
.text()
|
||||
.await
|
||||
.with_context(|| format!("Failed to decode RSS response body for '{}'", url))?;
|
||||
let mail_rss = tokio::task::spawn_blocking(move || quick_xml::de::from_str::<MailRss>(&body))
|
||||
.await?
|
||||
.with_context(|| format!("Failed to parse RSS feed for '{}'", url))?;
|
||||
let items = mail_rss
|
||||
.channel
|
||||
.items
|
||||
.into_iter()
|
||||
.rev()
|
||||
.skip_while(|item| item.pub_date.value <= list_state.last_pub_date)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for room in rooms {
|
||||
for item in &items {
|
||||
if publish_on_replies || !item.title.starts_with("Re: ") {
|
||||
let msg = RoomMessageEventContent::text_markdown(format!(
|
||||
"\\[{}\\] [{}]({})",
|
||||
list.name, item.title, item.link
|
||||
));
|
||||
room.send(msg, None)
|
||||
.await
|
||||
.with_context(|| format!("Failed to send message to room '{}'", room.room_id()))?;
|
||||
}
|
||||
save_list_state(
|
||||
ListState {
|
||||
last_pub_date: item.pub_date.value,
|
||||
},
|
||||
state_file,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn start_polling(config: MailArchiveConfig, matrix_client: Client) -> anyhow::Result<Vec<JoinHandle<()>>> {
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(format!("{}/{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")))
|
||||
.gzip(true)
|
||||
.redirect(redirect::Policy::default())
|
||||
.timeout(Duration::from_secs(8))
|
||||
.build()?;
|
||||
|
||||
Ok(config
|
||||
.lists
|
||||
.into_iter()
|
||||
.map(|list| {
|
||||
let room_ids = if list.rooms.is_empty() {
|
||||
&config.default_rooms
|
||||
} else {
|
||||
&list.rooms
|
||||
}
|
||||
.clone();
|
||||
let list = list.clone();
|
||||
let http_client = http_client.clone();
|
||||
let matrix_client = matrix_client.clone();
|
||||
let url = format!("https://www.mail-archive.com/{}/maillist.xml", list.name);
|
||||
let state_file = config.state_dir.join(format!("{}.state", list.name));
|
||||
let update_interval = Duration::from_secs(config.update_interval);
|
||||
|
||||
tokio::spawn(async move {
|
||||
if !room_ids.is_empty() {
|
||||
loop {
|
||||
if let Err(err) = handle_list(
|
||||
&list,
|
||||
&state_file,
|
||||
&http_client,
|
||||
&url,
|
||||
&matrix_client,
|
||||
list.publish_on_replies,
|
||||
&room_ids,
|
||||
)
|
||||
.await
|
||||
{
|
||||
warn!("{:#}", err);
|
||||
}
|
||||
|
||||
sleep(update_interval).await;
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn deser_rfc2616<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
struct Rfc2616Visitor;
|
||||
|
||||
impl<'de> de::Visitor<'de> for Rfc2616Visitor {
|
||||
type Value = DateTime<Utc>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("an RFC2616-formatted datetime")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
dateparser::parse(v).map_err(E::custom)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(Rfc2616Visitor)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::{fs::File, io::BufReader};
|
||||
|
||||
use super::MailRss;
|
||||
|
||||
#[test]
|
||||
pub fn test_feed_deser() -> anyhow::Result<()> {
|
||||
let f = File::open(format!("{}/test-data/maillist.xml", env!("CARGO_MANIFEST_DIR")))?;
|
||||
let r = BufReader::new(f);
|
||||
let mail_rss = quick_xml::de::from_reader::<_, MailRss>(r)?;
|
||||
println!("{:#?}", mail_rss);
|
||||
Ok(())
|
||||
}
|
||||
}
|
232
src/main.rs
232
src/main.rs
@ -22,226 +22,50 @@ extern crate log;
|
||||
extern crate serde;
|
||||
|
||||
mod config;
|
||||
mod event;
|
||||
mod gitlab_event;
|
||||
mod gitlab_webhook;
|
||||
mod mail_archive;
|
||||
mod matrix;
|
||||
|
||||
use std::{env, net::IpAddr, process::exit, sync::Arc, time::Duration};
|
||||
use std::{env, net::IpAddr, process::exit};
|
||||
|
||||
use anyhow::Context;
|
||||
use constant_time_eq::constant_time_eq;
|
||||
use event::{GitlabEvent, GitlabEventExt};
|
||||
use http::StatusCode;
|
||||
use matrix_sdk::{
|
||||
config::SyncSettings,
|
||||
room::Joined,
|
||||
ruma::{events::room::message::RoomMessageEventContent, OwnedRoomOrAliasId},
|
||||
BaseRoom, Client,
|
||||
};
|
||||
use tokio::sync::mpsc;
|
||||
use futures::future::join_all;
|
||||
use warp::Filter;
|
||||
|
||||
async fn build_sync_settings(matrix_client: &Client) -> SyncSettings {
|
||||
let mut settings = SyncSettings::default().timeout(Duration::from_secs(30));
|
||||
if let Some(token) = matrix_client.sync_token().await {
|
||||
settings = settings.token(token);
|
||||
}
|
||||
settings
|
||||
}
|
||||
|
||||
async fn matrix_connect(config: &config::Config) -> anyhow::Result<Client> {
|
||||
let client = Client::builder()
|
||||
.server_name(config.user_id.server_name())
|
||||
.user_agent(format!("{}/{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")))
|
||||
.build()
|
||||
.await?;
|
||||
client
|
||||
.login_username(&config.user_id, &config.password)
|
||||
.initial_device_display_name("Bebot")
|
||||
.send()
|
||||
.await?;
|
||||
info!("Connected to matrix as {}; waiting for first sync", config.user_id);
|
||||
|
||||
let settings = build_sync_settings(&client).await;
|
||||
client.sync_once(settings).await?;
|
||||
info!("First matrix sync complete");
|
||||
|
||||
let sync_client = client.clone();
|
||||
tokio::spawn(async move {
|
||||
let settings = build_sync_settings(&sync_client).await;
|
||||
if let Err(err) = sync_client.sync(settings).await {
|
||||
error!("Matrix sync failed: {}", err);
|
||||
exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
async fn ensure_matrix_room_joined(matrix_client: &Client, room_id: &OwnedRoomOrAliasId) -> anyhow::Result<Joined> {
|
||||
fn room_matches(a_room: &BaseRoom, our_room: &OwnedRoomOrAliasId) -> bool {
|
||||
let our_room_str = our_room.as_str();
|
||||
a_room.room_id().as_str() == our_room_str
|
||||
|| a_room
|
||||
.canonical_alias()
|
||||
.iter()
|
||||
.any(|alias| alias.as_str() == our_room_str)
|
||||
|| a_room.alt_aliases().iter().any(|alias| alias.as_str() == our_room_str)
|
||||
}
|
||||
|
||||
let mut room = matrix_client
|
||||
.joined_rooms()
|
||||
.iter()
|
||||
.find(|a_room| room_matches(a_room, room_id))
|
||||
.cloned();
|
||||
if room.is_none() {
|
||||
if let Some(invited) = matrix_client
|
||||
.invited_rooms()
|
||||
.iter()
|
||||
.find(|a_room| room_matches(a_room, room_id))
|
||||
{
|
||||
invited.accept_invitation().await?;
|
||||
} else {
|
||||
matrix_client.join_room_by_id_or_alias(room_id, &[]).await?;
|
||||
}
|
||||
let settings = build_sync_settings(matrix_client).await;
|
||||
matrix_client.sync_once(settings).await?;
|
||||
room = matrix_client
|
||||
.joined_rooms()
|
||||
.iter()
|
||||
.find(|a_room| room_matches(a_room, room_id))
|
||||
.cloned();
|
||||
}
|
||||
|
||||
room.ok_or_else(|| anyhow!("Unable to join room {}", room_id))
|
||||
}
|
||||
|
||||
fn build_gitlab_messages(event: &GitlabEvent) -> Vec<String> {
|
||||
let project = event.project();
|
||||
let refname = event.r#ref().map(event::parse_ref);
|
||||
event
|
||||
.titles()
|
||||
.iter()
|
||||
.map(|title| {
|
||||
format!(
|
||||
"\\[{}\\] {}*{}* {}",
|
||||
project.path_with_namespace,
|
||||
refname
|
||||
.as_ref()
|
||||
.map(|rn| format!("`{}` ", rn))
|
||||
.unwrap_or_else(|| "".to_string()),
|
||||
event.user(),
|
||||
title,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
async fn handle_gitlab_event(
|
||||
event: GitlabEvent,
|
||||
room_id: &OwnedRoomOrAliasId,
|
||||
matrix_client: &Client,
|
||||
) -> anyhow::Result<()> {
|
||||
let room = ensure_matrix_room_joined(matrix_client, room_id).await?;
|
||||
for msg in build_gitlab_messages(&event) {
|
||||
debug!("Sending message to {}: {}", room_id, msg);
|
||||
let msg_content = RoomMessageEventContent::text_markdown(&msg);
|
||||
room.send(msg_content, None).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn run() -> anyhow::Result<()> {
|
||||
let config_path = env::args()
|
||||
.nth(1)
|
||||
.ok_or_else(|| anyhow!("Config file should be passed as only parameter"))?;
|
||||
let config = Arc::new(config::load(config_path).await?);
|
||||
let addr = config
|
||||
.bind_address
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| "127.0.0.1".to_string())
|
||||
.parse::<IpAddr>()
|
||||
.context("Failed to parse bind_address")?;
|
||||
let port = config.bind_port.unwrap_or(3000);
|
||||
let mut config = config::load(config_path).await?;
|
||||
|
||||
let matrix_client = matrix_connect(&config).await.context("Failed to connect to Matrix")?;
|
||||
let matrix_client = matrix::connect(&config).await.context("Failed to connect to Matrix")?;
|
||||
|
||||
let (event_tx, mut event_rx) = mpsc::channel::<(GitlabEvent, OwnedRoomOrAliasId)>(100);
|
||||
tokio::spawn(async move {
|
||||
while let Some((event, room)) = event_rx.recv().await {
|
||||
if let Err(err) = handle_gitlab_event(event, &room, &matrix_client).await {
|
||||
warn!("Failed to handle payload: {}", err);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let gitlab_root_path = if let Some(url_prefix) = config.url_prefix.as_ref() {
|
||||
url_prefix.split('/').fold(warp::any().boxed(), |last, segment| {
|
||||
if segment.is_empty() {
|
||||
last
|
||||
} else {
|
||||
last.and(warp::path(segment.to_string())).boxed()
|
||||
}
|
||||
})
|
||||
let handles = if let Some(mail_archive) = config.mail_archive.take() {
|
||||
mail_archive::start_polling(mail_archive, matrix_client.clone())?
|
||||
} else {
|
||||
warp::any().boxed()
|
||||
vec![]
|
||||
};
|
||||
|
||||
let gitlab = gitlab_root_path
|
||||
.and(warp::path!("hooks" / "gitlab"))
|
||||
.and(warp::post())
|
||||
.and(warp::header::<String>("x-gitlab-token"))
|
||||
.and(warp::body::json())
|
||||
.then(move |token: String, event: event::GitlabEvent| {
|
||||
let config = Arc::clone(&config);
|
||||
let event_tx = event_tx.clone();
|
||||
if let Some(gitlab_webhook) = config.gitlab_webhook.take() {
|
||||
let gitlab = gitlab_webhook::build_route(gitlab_webhook, matrix_client.clone())?;
|
||||
let routes = gitlab.with(warp::log("bebot"));
|
||||
|
||||
async move {
|
||||
match event {
|
||||
GitlabEvent::Other => {
|
||||
warp::reply::with_status("Unsupported Gitlab event type", StatusCode::BAD_REQUEST)
|
||||
}
|
||||
_ => {
|
||||
let project = event.project();
|
||||
let config_key = project.web_url.replace("http://", "").replace("https://", "");
|
||||
if let Some(repo_config) = config.repo_configs.get(&config_key) {
|
||||
if !constant_time_eq(token.as_bytes(), repo_config.token.as_bytes()) {
|
||||
warn!("Invalid token for repo '{}'", config_key);
|
||||
warp::reply::with_status("Invalid token", StatusCode::FORBIDDEN)
|
||||
} else {
|
||||
debug!("payload: {:?}", event);
|
||||
if let Some(room) = repo_config.room.as_ref().or(config.default_room.as_ref()) {
|
||||
let publish_events = repo_config
|
||||
.publish_events
|
||||
.as_ref()
|
||||
.or(config.default_publish_events.as_ref());
|
||||
if publish_events.map(|ecs| event.should_publish(ecs)).unwrap_or(true) {
|
||||
if let Err(err) = event_tx.send((event, room.clone())).await {
|
||||
warn!("Failed to enqueue payload: {}", err);
|
||||
}
|
||||
}
|
||||
warp::reply::with_status("OK", StatusCode::OK)
|
||||
} else {
|
||||
info!("Channel not configured for repo '{}'", config_key);
|
||||
warp::reply::with_status(
|
||||
"Matrix room not configured for repo",
|
||||
StatusCode::NOT_FOUND,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
info!("Repo '{}' unconfigured", config_key);
|
||||
warp::reply::with_status("Repo not configured", StatusCode::NOT_FOUND)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
let addr = config
|
||||
.bind_address
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| "127.0.0.1".to_string())
|
||||
.parse::<IpAddr>()
|
||||
.context("Failed to parse bind_address")?;
|
||||
let port = config.bind_port.unwrap_or(3000);
|
||||
warp::serve(routes).run((addr, port)).await;
|
||||
}
|
||||
|
||||
let routes = gitlab.with(warp::log("bebot"));
|
||||
join_all(handles).await;
|
||||
|
||||
warp::serve(routes).run((addr, port)).await;
|
||||
|
||||
Ok(())
|
||||
error!("No functionality is configured; exiting");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
@ -252,7 +76,7 @@ async fn main() {
|
||||
env_logger::init_from_env(lenv);
|
||||
|
||||
if let Err(err) = run().await {
|
||||
error!("{}", err);
|
||||
error!("{:#}", err);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
188
src/matrix.rs
Normal file
188
src/matrix.rs
Normal file
@ -0,0 +1,188 @@
|
||||
// bebot
|
||||
// Copyright (C) 2023 Brian Tarricone <brian@tarricone.org>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{fmt, process::exit, time::Duration};
|
||||
|
||||
use matrix_sdk::{
|
||||
config::SyncSettings,
|
||||
room::Joined,
|
||||
ruma::{OwnedRoomOrAliasId, OwnedUserId, RoomOrAliasId, UserId},
|
||||
BaseRoom, Client,
|
||||
};
|
||||
use serde::de;
|
||||
|
||||
use crate::config::Config;
|
||||
|
||||
async fn build_sync_settings(matrix_client: &Client) -> SyncSettings {
|
||||
let mut settings = SyncSettings::default().timeout(Duration::from_secs(30));
|
||||
if let Some(token) = matrix_client.sync_token().await {
|
||||
settings = settings.token(token);
|
||||
}
|
||||
settings
|
||||
}
|
||||
|
||||
pub async fn connect(config: &Config) -> anyhow::Result<Client> {
|
||||
let client = Client::builder()
|
||||
.server_name(config.user_id.server_name())
|
||||
.user_agent(format!("{}/{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")))
|
||||
.build()
|
||||
.await?;
|
||||
client
|
||||
.login_username(&config.user_id, &config.password)
|
||||
.initial_device_display_name("Bebot")
|
||||
.send()
|
||||
.await?;
|
||||
info!("Connected to matrix as {}; waiting for first sync", config.user_id);
|
||||
|
||||
let settings = build_sync_settings(&client).await;
|
||||
client.sync_once(settings).await?;
|
||||
info!("First matrix sync complete");
|
||||
|
||||
let sync_client = client.clone();
|
||||
tokio::spawn(async move {
|
||||
let settings = build_sync_settings(&sync_client).await;
|
||||
if let Err(err) = sync_client.sync(settings).await {
|
||||
error!("Matrix sync failed: {}", err);
|
||||
exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
pub async fn ensure_room_joined(matrix_client: &Client, room_id: &OwnedRoomOrAliasId) -> anyhow::Result<Joined> {
|
||||
fn room_matches(a_room: &BaseRoom, our_room: &OwnedRoomOrAliasId) -> bool {
|
||||
let our_room_str = our_room.as_str();
|
||||
a_room.room_id().as_str() == our_room_str
|
||||
|| a_room
|
||||
.canonical_alias()
|
||||
.iter()
|
||||
.any(|alias| alias.as_str() == our_room_str)
|
||||
|| a_room.alt_aliases().iter().any(|alias| alias.as_str() == our_room_str)
|
||||
}
|
||||
|
||||
let mut room = matrix_client
|
||||
.joined_rooms()
|
||||
.iter()
|
||||
.find(|a_room| room_matches(a_room, room_id))
|
||||
.cloned();
|
||||
if room.is_none() {
|
||||
if let Some(invited) = matrix_client
|
||||
.invited_rooms()
|
||||
.iter()
|
||||
.find(|a_room| room_matches(a_room, room_id))
|
||||
{
|
||||
invited.accept_invitation().await?;
|
||||
} else {
|
||||
matrix_client.join_room_by_id_or_alias(room_id, &[]).await?;
|
||||
}
|
||||
let settings = build_sync_settings(matrix_client).await;
|
||||
matrix_client.sync_once(settings).await?;
|
||||
room = matrix_client
|
||||
.joined_rooms()
|
||||
.iter()
|
||||
.find(|a_room| room_matches(a_room, room_id))
|
||||
.cloned();
|
||||
}
|
||||
|
||||
room.ok_or_else(|| anyhow!("Unable to join room {}", room_id))
|
||||
}
|
||||
|
||||
pub fn deser_user_id<'de, D>(deserializer: D) -> Result<OwnedUserId, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
struct UserIdVisitor;
|
||||
|
||||
impl<'de> de::Visitor<'de> for UserIdVisitor {
|
||||
type Value = OwnedUserId;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a matrix user ID")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
UserId::parse(v).map_err(E::custom)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(UserIdVisitor)
|
||||
}
|
||||
|
||||
fn deser_room_or_alias_id<'de, D>(deserializer: D) -> Result<OwnedRoomOrAliasId, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
struct RoomOrAliasIdVisitor;
|
||||
|
||||
impl<'de> de::Visitor<'de> for RoomOrAliasIdVisitor {
|
||||
type Value = OwnedRoomOrAliasId;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a matrix room ID")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
RoomOrAliasId::parse(v).map_err(E::custom)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(RoomOrAliasIdVisitor)
|
||||
}
|
||||
|
||||
pub fn deser_optional_room_or_alias_id<'de, D>(deserializer: D) -> Result<Option<OwnedRoomOrAliasId>, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
struct OptionalRoomOrAliasIdVisitor;
|
||||
|
||||
impl<'de> de::Visitor<'de> for OptionalRoomOrAliasIdVisitor {
|
||||
type Value = Option<OwnedRoomOrAliasId>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("null or matrix room ID")
|
||||
}
|
||||
|
||||
fn visit_none<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
Ok(Some(deser_room_or_alias_id(deserializer)?))
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
RoomOrAliasId::parse(v).map(Some).map_err(E::custom)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(OptionalRoomOrAliasIdVisitor)
|
||||
}
|
722
test-data/maillist.xml
Normal file
722
test-data/maillist.xml
Normal file
@ -0,0 +1,722 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0">
|
||||
<!-- MHonArc v2.6.19+ -->
|
||||
<channel>
|
||||
<title>xfce-announce</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org</link>
|
||||
<description>xfce-announce @ xfce</description>
|
||||
<pubDate>Wed, 20 Sep 2023 00:05:30 GMT</pubDate>
|
||||
<lastBuildDate>Wed, 20 Sep 2023 00:05:30 GMT</lastBuildDate>
|
||||
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
|
||||
<generator>MHonArc RSS 2.0 RCFile</generator>
|
||||
<webMaster>themailarchive@gmail.com (The Mail Archive)</webMaster>
|
||||
<image>
|
||||
<title>The Mail Archive</title>
|
||||
<url>http://www.mail-archive.com/nanologo.png</url>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org</link>
|
||||
</image>
|
||||
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-notifyd 0.9.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00875.html</link>
|
||||
<description><font color=#6f6f6f> 2023/09/20</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Brian Tarricone%22">Brian Tarricone</a></description>
|
||||
<pubDate>Wed, 20 Sep 2023 00:05:29 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00875.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-notifyd 0.9.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00874.html</link>
|
||||
<description><font color=#6f6f6f> 2023/09/18</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Brian Tarricone%22">Brian Tarricone</a></description>
|
||||
<pubDate>Mon, 18 Sep 2023 21:21:54 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00874.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-appfinder 4.19.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00873.html</link>
|
||||
<description><font color=#6f6f6f> 2023/09/08</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Andre Miranda%22">Andre Miranda</a></description>
|
||||
<pubDate>Fri, 08 Sep 2023 21:21:14 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00873.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: thunar 4.18.7 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00872.html</link>
|
||||
<description><font color=#6f6f6f> 2023/09/01</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Alexander Schwinn%22">Alexander Schwinn</a></description>
|
||||
<pubDate>Fri, 01 Sep 2023 22:48:46 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00872.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-whiskermenu-plugin 2.8.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00871.html</link>
|
||||
<description><font color=#6f6f6f> 2023/08/29</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Graeme Gott%22">Graeme Gott</a></description>
|
||||
<pubDate>Tue, 29 Aug 2023 14:34:06 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00871.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-appfinder 4.18.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00870.html</link>
|
||||
<description><font color=#6f6f6f> 2023/08/22</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Andre Miranda%22">Andre Miranda</a></description>
|
||||
<pubDate>Tue, 22 Aug 2023 19:37:53 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00870.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-terminal 1.1.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00869.html</link>
|
||||
<description><font color=#6f6f6f> 2023/08/22</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Tue, 22 Aug 2023 09:33:15 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00869.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: catfish 4.18.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00868.html</link>
|
||||
<description><font color=#6f6f6f> 2023/08/06</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Sun, 06 Aug 2023 09:01:49 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00868.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-clipman-plugin 1.6.4 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00867.html</link>
|
||||
<description><font color=#6f6f6f> 2023/07/31</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Mon, 31 Jul 2023 13:56:43 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00867.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-volumed-pulse 0.2.4 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00866.html</link>
|
||||
<description><font color=#6f6f6f> 2023/07/25</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Tue, 25 Jul 2023 11:10:24 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00866.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-cpugraph-plugin 1.2.8 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00865.html</link>
|
||||
<description><font color=#6f6f6f> 2023/07/22</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Błażej Szczygieł%22">Błażej Szczygieł</a></description>
|
||||
<pubDate>Sat, 22 Jul 2023 20:59:22 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00865.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-verve-plugin 2.0.3 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00864.html</link>
|
||||
<description><font color=#6f6f6f> 2023/07/15</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Sat, 15 Jul 2023 18:29:04 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00864.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-settings 4.18.3 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00863.html</link>
|
||||
<description><font color=#6f6f6f> 2023/07/04</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Tue, 04 Jul 2023 08:10:25 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00863.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-panel-profiles 1.0.14 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00862.html</link>
|
||||
<description><font color=#6f6f6f> 2023/06/05</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Mon, 05 Jun 2023 07:06:36 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00862.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-pulseaudio-plugin 0.4.7 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00861.html</link>
|
||||
<description><font color=#6f6f6f> 2023/06/01</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Błażej Szczygieł%22">Błażej Szczygieł</a></description>
|
||||
<pubDate>Thu, 01 Jun 2023 11:41:25 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00861.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-screensaver 4.18.2 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00860.html</link>
|
||||
<description><font color=#6f6f6f> 2023/05/30</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Tue, 30 May 2023 08:01:59 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00860.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-power-manager 4.19.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00859.html</link>
|
||||
<description><font color=#6f6f6f> 2023/05/30</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Tue, 30 May 2023 07:56:50 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00859.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-power-manager 4.18.2 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00858.html</link>
|
||||
<description><font color=#6f6f6f> 2023/05/30</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Tue, 30 May 2023 07:47:19 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00858.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-session 4.19.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00857.html</link>
|
||||
<description><font color=#6f6f6f> 2023/05/29</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Mon, 29 May 2023 16:08:10 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00857.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: libxfce4ui 4.19.2 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00856.html</link>
|
||||
<description><font color=#6f6f6f> 2023/05/29</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Mon, 29 May 2023 15:56:04 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00856.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-session 4.18.3 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00855.html</link>
|
||||
<description><font color=#6f6f6f> 2023/05/29</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Mon, 29 May 2023 15:49:40 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00855.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-generic-slider 1.0.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00854.html</link>
|
||||
<description><font color=#6f6f6f> 2023/05/26</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Fri, 26 May 2023 17:06:57 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00854.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-panel 4.18.4 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00853.html</link>
|
||||
<description><font color=#6f6f6f> 2023/05/22</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Mon, 22 May 2023 17:45:02 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00853.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: libxfce4ui 4.18.4 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00852.html</link>
|
||||
<description><font color=#6f6f6f> 2023/05/22</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Mon, 22 May 2023 17:06:08 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00852.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-screenshooter 1.10.4 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00851.html</link>
|
||||
<description><font color=#6f6f6f> 2023/05/14</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Andre Miranda%22">Andre Miranda</a></description>
|
||||
<pubDate>Sun, 14 May 2023 11:31:56 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00851.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: ristretto 0.13.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00850.html</link>
|
||||
<description><font color=#6f6f6f> 2023/05/13</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Sat, 13 May 2023 15:59:23 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00850.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: mousepad 0.6.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00849.html</link>
|
||||
<description><font color=#6f6f6f> 2023/05/13</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Sat, 13 May 2023 15:45:22 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00849.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: libxfce4util 4.19.2 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00848.html</link>
|
||||
<description><font color=#6f6f6f> 2023/05/11</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Thu, 11 May 2023 14:14:17 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00848.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: libxfce4util 4.19.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00847.html</link>
|
||||
<description><font color=#6f6f6f> 2023/05/07</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Sun, 07 May 2023 08:39:15 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00847.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-mixer 4.18.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00846.html</link>
|
||||
<description><font color=#6f6f6f> 2023/05/01</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Mon, 01 May 2023 18:21:54 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00846.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: thunar 4.18.6 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00845.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/30</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Alexander Schwinn%22">Alexander Schwinn</a></description>
|
||||
<pubDate>Sun, 30 Apr 2023 09:34:07 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00845.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-netload-plugin 1.4.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00844.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/29</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Sat, 29 Apr 2023 06:28:16 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00844.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-mpc-plugin 0.5.3 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00843.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/29</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Sat, 29 Apr 2023 06:24:59 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00843.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-mixer 4.18.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00842.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/29</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Sat, 29 Apr 2023 06:16:57 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00842.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: thunar 4.18.5 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00841.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/28</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Alexander Schwinn%22">Alexander Schwinn</a></description>
|
||||
<pubDate>Fri, 28 Apr 2023 20:50:14 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00841.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-whiskermenu-plugin 2.7.3 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00840.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/26</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Graeme Gott%22">Graeme Gott</a></description>
|
||||
<pubDate>Wed, 26 Apr 2023 11:25:21 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00840.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-dict 0.8.5 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00839.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/22</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Sat, 22 Apr 2023 14:59:42 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00839.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfmpc 0.3.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00838.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/22</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Sat, 22 Apr 2023 14:54:03 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00838.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-weather-plugin 0.11.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00837.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/21</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Fri, 21 Apr 2023 18:51:58 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00837.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-time-out-plugin 1.1.3 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00836.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/21</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Fri, 21 Apr 2023 17:27:47 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00836.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-indicator-plugin 2.4.2 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00835.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/21</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Fri, 21 Apr 2023 09:51:53 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00835.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-mount-plugin 1.1.6 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00834.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/20</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Thu, 20 Apr 2023 21:19:57 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00834.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: libxfce4ui 4.19.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00833.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/20</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Thu, 20 Apr 2023 20:17:14 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00833.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-mailwatch-plugin 1.3.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00832.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/20</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Thu, 20 Apr 2023 18:43:40 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00832.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-fsguard-plugin 1.1.3 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00831.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/19</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Wed, 19 Apr 2023 17:30:01 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00831.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-calculator-plugin 0.7.2 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00830.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/19</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Wed, 19 Apr 2023 17:23:59 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00830.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-battery-plugin 1.1.5 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00829.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/19</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Wed, 19 Apr 2023 16:55:39 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00829.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-appfinder 4.19.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00828.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/19</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Andre Miranda%22">Andre Miranda</a></description>
|
||||
<pubDate>Wed, 19 Apr 2023 16:34:38 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00828.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-timer-plugin 1.7.2 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00827.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/18</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Tue, 18 Apr 2023 16:03:44 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00827.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-power-manager 4.19.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00826.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/18</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Tue, 18 Apr 2023 15:08:59 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00826.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-session 4.19.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00825.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/18</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Tue, 18 Apr 2023 14:52:59 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00825.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-settings 4.19.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00824.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/18</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Tue, 18 Apr 2023 11:30:02 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00824.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: garcon 4.19.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00823.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/18</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Tue, 18 Apr 2023 11:18:13 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00823.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: libxfce4ui 4.19.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00822.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/17</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Mon, 17 Apr 2023 14:22:29 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00822.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: libxfce4util 4.19.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00821.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/17</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Mon, 17 Apr 2023 11:25:12 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00821.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: tumbler 4.19.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00820.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/17</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Mon, 17 Apr 2023 11:10:57 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00820.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfconf 4.19.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00819.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/17</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Mon, 17 Apr 2023 11:06:03 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00819.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: tumbler 4.18.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00818.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/17</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Mon, 17 Apr 2023 10:56:58 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00818.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfconf 4.18.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00817.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/17</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Mon, 17 Apr 2023 10:38:46 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00817.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-genmon-plugin 4.2.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00816.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/16</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Tony Paulic%22">Tony Paulic</a></description>
|
||||
<pubDate>Sun, 16 Apr 2023 23:34:56 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00816.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfdesktop 4.19.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00815.html</link>
|
||||
<description><font color=#6f6f6f> 2023/04/14</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Brian Tarricone%22">Brian Tarricone</a></description>
|
||||
<pubDate>Fri, 14 Apr 2023 22:55:25 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00815.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: thunar-archive-plugin 0.5.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00814.html</link>
|
||||
<description><font color=#6f6f6f> 2023/03/31</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Alexander Schwinn%22">Alexander Schwinn</a></description>
|
||||
<pubDate>Fri, 31 Mar 2023 21:18:14 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00814.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-panel 4.19.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00813.html</link>
|
||||
<description><font color=#6f6f6f> 2023/03/28</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Tue, 28 Mar 2023 19:08:27 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00813.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-pulseaudio-plugin 0.4.6 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00812.html</link>
|
||||
<description><font color=#6f6f6f> 2023/03/28</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Tue, 28 Mar 2023 19:05:50 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00812.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-panel 4.18.3 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00811.html</link>
|
||||
<description><font color=#6f6f6f> 2023/03/28</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Tue, 28 Mar 2023 17:57:50 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00811.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: garcon 4.18.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00810.html</link>
|
||||
<description><font color=#6f6f6f> 2023/03/28</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Tue, 28 Mar 2023 17:41:11 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00810.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: libxfce4ui 4.18.3 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00809.html</link>
|
||||
<description><font color=#6f6f6f> 2023/03/28</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Tue, 28 Mar 2023 17:21:43 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00809.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-session 4.18.2 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00808.html</link>
|
||||
<description><font color=#6f6f6f> 2023/03/28</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Tue, 28 Mar 2023 17:13:19 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00808.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-screensaver 4.18.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00807.html</link>
|
||||
<description><font color=#6f6f6f> 2023/03/26</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Sun, 26 Mar 2023 20:36:45 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00807.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>Re: ANNOUNCE: xfce4-docklike-plugin 0.4.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00806.html</link>
|
||||
<description><font color=#6f6f6f> 2023/03/21</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Luna Jernberg%22">Luna Jernberg</a></description>
|
||||
<pubDate>Tue, 21 Mar 2023 07:53:23 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00806.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-clipman-plugin 1.6.3 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00805.html</link>
|
||||
<description><font color=#6f6f6f> 2023/03/20</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Mon, 20 Mar 2023 20:07:46 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00805.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-docklike-plugin 0.4.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00804.html</link>
|
||||
<description><font color=#6f6f6f> 2023/03/19</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Sun, 19 Mar 2023 16:41:45 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00804.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-screensaver 4.18.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00803.html</link>
|
||||
<description><font color=#6f6f6f> 2023/03/18</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Sat, 18 Mar 2023 10:47:52 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00803.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-notes-plugin 1.10.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00802.html</link>
|
||||
<description><font color=#6f6f6f> 2023/03/18</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Sat, 18 Mar 2023 10:12:20 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00802.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-dev-tools 4.19.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00801.html</link>
|
||||
<description><font color=#6f6f6f> 2023/03/05</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Brian Tarricone%22">Brian Tarricone</a></description>
|
||||
<pubDate>Sun, 05 Mar 2023 08:52:26 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00801.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfburn 0.7.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00800.html</link>
|
||||
<description><font color=#6f6f6f> 2023/03/04</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Hunter Turcin%22">Hunter Turcin</a></description>
|
||||
<pubDate>Sat, 04 Mar 2023 07:40:15 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00800.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-notifyd 0.8.2 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00799.html</link>
|
||||
<description><font color=#6f6f6f> 2023/02/28</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Brian Tarricone%22">Brian Tarricone</a></description>
|
||||
<pubDate>Tue, 28 Feb 2023 06:39:12 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00799.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: thunar 4.18.4 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00798.html</link>
|
||||
<description><font color=#6f6f6f> 2023/02/22</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Alexander Schwinn%22">Alexander Schwinn</a></description>
|
||||
<pubDate>Wed, 22 Feb 2023 21:57:16 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00798.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: parole 4.18.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00797.html</link>
|
||||
<description><font color=#6f6f6f> 2023/02/18</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Sat, 18 Feb 2023 19:39:06 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00797.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-notifyd 0.8.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00796.html</link>
|
||||
<description><font color=#6f6f6f> 2023/02/17</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Brian Tarricone%22">Brian Tarricone</a></description>
|
||||
<pubDate>Fri, 17 Feb 2023 03:26:47 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00796.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-settings 4.18.2 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00795.html</link>
|
||||
<description><font color=#6f6f6f> 2023/02/11</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Sat, 11 Feb 2023 09:36:19 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00795.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-notifyd 0.8.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00794.html</link>
|
||||
<description><font color=#6f6f6f> 2023/02/11</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Brian Tarricone%22">Brian Tarricone</a></description>
|
||||
<pubDate>Sat, 11 Feb 2023 07:51:19 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00794.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-power-manager 4.18.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00793.html</link>
|
||||
<description><font color=#6f6f6f> 2023/02/10</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Fri, 10 Feb 2023 10:23:06 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00793.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-session 4.18.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00792.html</link>
|
||||
<description><font color=#6f6f6f> 2023/02/10</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Fri, 10 Feb 2023 10:14:48 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00792.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: ristretto 0.13.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00791.html</link>
|
||||
<description><font color=#6f6f6f> 2023/02/09</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Thu, 09 Feb 2023 17:41:13 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00791.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: mousepad 0.6.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00790.html</link>
|
||||
<description><font color=#6f6f6f> 2023/02/09</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Thu, 09 Feb 2023 17:21:04 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00790.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-panel 4.18.2 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00789.html</link>
|
||||
<description><font color=#6f6f6f> 2023/02/09</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Thu, 09 Feb 2023 17:01:36 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00789.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: libxfce4ui 4.18.2 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00788.html</link>
|
||||
<description><font color=#6f6f6f> 2023/02/09</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Thu, 09 Feb 2023 11:06:57 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00788.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: orage 4.18.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00787.html</link>
|
||||
<description><font color=#6f6f6f> 2023/02/08</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Wed, 08 Feb 2023 16:21:58 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00787.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: thunar-media-tags-plugin 0.4.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00786.html</link>
|
||||
<description><font color=#6f6f6f> 2023/02/06</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Alexander Schwinn%22">Alexander Schwinn</a></description>
|
||||
<pubDate>Mon, 06 Feb 2023 16:10:00 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00786.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-notifyd 0.7.3 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00785.html</link>
|
||||
<description><font color=#6f6f6f> 2023/02/02</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Brian Tarricone%22">Brian Tarricone</a></description>
|
||||
<pubDate>Thu, 02 Feb 2023 03:07:52 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00785.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-notifyd 0.7.2 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00784.html</link>
|
||||
<description><font color=#6f6f6f> 2023/01/22</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Brian Tarricone%22">Brian Tarricone</a></description>
|
||||
<pubDate>Sun, 22 Jan 2023 08:59:46 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00784.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfdesktop 4.18.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00783.html</link>
|
||||
<description><font color=#6f6f6f> 2023/01/21</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Brian Tarricone%22">Brian Tarricone</a></description>
|
||||
<pubDate>Sat, 21 Jan 2023 12:04:24 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00783.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-notifyd 0.7.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00782.html</link>
|
||||
<description><font color=#6f6f6f> 2023/01/21</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Brian Tarricone%22">Brian Tarricone</a></description>
|
||||
<pubDate>Sat, 21 Jan 2023 11:50:36 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00782.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-notifyd 0.7.0 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00781.html</link>
|
||||
<description><font color=#6f6f6f> 2023/01/21</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Brian Tarricone%22">Brian Tarricone</a></description>
|
||||
<pubDate>Sat, 21 Jan 2023 11:26:06 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00781.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: thunar 4.18.3 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00780.html</link>
|
||||
<description><font color=#6f6f6f> 2023/01/20</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Alexander Schwinn%22">Alexander Schwinn</a></description>
|
||||
<pubDate>Fri, 20 Jan 2023 22:43:03 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00780.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-whiskermenu-plugin 2.7.2 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00779.html</link>
|
||||
<description><font color=#6f6f6f> 2023/01/17</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Graeme Gott%22">Graeme Gott</a></description>
|
||||
<pubDate>Tue, 17 Jan 2023 18:24:47 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00779.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-datetime-plugin 0.8.3 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00778.html</link>
|
||||
<description><font color=#6f6f6f> 2023/01/16</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Andre Miranda%22">Andre Miranda</a></description>
|
||||
<pubDate>Mon, 16 Jan 2023 21:21:48 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00778.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-screenshooter 1.10.3 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00777.html</link>
|
||||
<description><font color=#6f6f6f> 2023/01/12</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Andre Miranda%22">Andre Miranda</a></description>
|
||||
<pubDate>Thu, 12 Jan 2023 23:19:39 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00777.html</guid>
|
||||
</item>
|
||||
<item>
|
||||
<title>ANNOUNCE: xfce4-panel 4.18.1 released</title>
|
||||
<link>http://www.mail-archive.com/xfce-announce@xfce.org/msg00776.html</link>
|
||||
<description><font color=#6f6f6f> 2023/01/10</font> -- <a href="http://www.mail-archive.com/search?l=xfce-announce@xfce.org&q=from:%22Gaël Bonithon%22">Gaël Bonithon</a></description>
|
||||
<pubDate>Tue, 10 Jan 2023 21:04:35 GMT</pubDate>
|
||||
<guid isPermaLink="true">http://www.mail-archive.com/xfce-announce@xfce.org/msg00776.html</guid>
|
||||
</item>
|
||||
|
||||
</channel>
|
||||
</rss>
|
||||
<!-- MHonArc v2.6.19+ -->
|
Loading…
x
Reference in New Issue
Block a user