readme, some fixes
This commit is contained in:
+16
-11
@@ -134,7 +134,7 @@ pub fn parse_status_tags(content: &str) -> Vec<String> {
|
||||
let mut tags = vec![];
|
||||
for c in RE_A_HASHTAG.captures_iter(&content) {
|
||||
if let Some(s) = c.get(1) {
|
||||
tags.push(s.as_str().to_string())
|
||||
tags.push(s.as_str().to_lowercase())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,6 +150,11 @@ pub fn parse_slash_commands(content: &str) -> Vec<StatusCommand> {
|
||||
let content = voca_rs::strip::strip_tags(&content);
|
||||
debug!("Stripped tags: {}", content);
|
||||
|
||||
if !content.contains('/') && !content.contains('\\') {
|
||||
// No slash = no command
|
||||
return vec![];
|
||||
}
|
||||
|
||||
// short-circuiting commands
|
||||
|
||||
if RE_IGNORE.is_match(&content) {
|
||||
@@ -221,7 +226,7 @@ pub fn parse_slash_commands(content: &str) -> Vec<StatusCommand> {
|
||||
let s = s.as_str();
|
||||
let s = s.trim_start_matches('@');
|
||||
debug!("BAN USER: {}", s);
|
||||
commands.push(StatusCommand::BanUser(s.to_owned()));
|
||||
commands.push(StatusCommand::BanUser(s.to_lowercase()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,21 +235,21 @@ pub fn parse_slash_commands(content: &str) -> Vec<StatusCommand> {
|
||||
let s = s.as_str();
|
||||
let s = s.trim_start_matches('@');
|
||||
debug!("UNBAN USER: {}", s);
|
||||
commands.push(StatusCommand::UnbanUser(s.to_owned()));
|
||||
commands.push(StatusCommand::UnbanUser(s.to_lowercase()));
|
||||
}
|
||||
}
|
||||
|
||||
for c in RE_BAN_SERVER.captures_iter(&content) {
|
||||
if let Some(s) = c.get(1) {
|
||||
debug!("BAN SERVER: {}", s.as_str());
|
||||
commands.push(StatusCommand::BanServer(s.as_str().to_owned()));
|
||||
commands.push(StatusCommand::BanServer(s.as_str().to_lowercase()));
|
||||
}
|
||||
}
|
||||
|
||||
for c in RE_UNBAN_SERVER.captures_iter(&content) {
|
||||
if let Some(s) = c.get(1) {
|
||||
debug!("UNBAN SERVER: {}", s.as_str());
|
||||
commands.push(StatusCommand::UnbanServer(s.as_str().to_owned()));
|
||||
commands.push(StatusCommand::UnbanServer(s.as_str().to_lowercase()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,7 +258,7 @@ pub fn parse_slash_commands(content: &str) -> Vec<StatusCommand> {
|
||||
let s = s.as_str();
|
||||
let s = s.trim_start_matches('@');
|
||||
debug!("ADD MEMBER: {}", s);
|
||||
commands.push(StatusCommand::AddMember(s.to_owned()));
|
||||
commands.push(StatusCommand::AddMember(s.to_lowercase()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,7 +267,7 @@ pub fn parse_slash_commands(content: &str) -> Vec<StatusCommand> {
|
||||
let s = s.as_str();
|
||||
let s = s.trim_start_matches('@');
|
||||
debug!("REMOVE USER: {}", s);
|
||||
commands.push(StatusCommand::RemoveMember(s.to_owned()));
|
||||
commands.push(StatusCommand::RemoveMember(s.to_lowercase()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,7 +275,7 @@ pub fn parse_slash_commands(content: &str) -> Vec<StatusCommand> {
|
||||
if let Some(s) = c.get(1) {
|
||||
let s = s.as_str();
|
||||
debug!("ADD TAG: {}", s);
|
||||
commands.push(StatusCommand::AddTag(s.to_owned()));
|
||||
commands.push(StatusCommand::AddTag(s.to_lowercase()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +283,7 @@ pub fn parse_slash_commands(content: &str) -> Vec<StatusCommand> {
|
||||
if let Some(s) = c.get(1) {
|
||||
let s = s.as_str();
|
||||
debug!("REMOVE TAG: {}", s);
|
||||
commands.push(StatusCommand::RemoveTag(s.to_owned()));
|
||||
commands.push(StatusCommand::RemoveTag(s.to_lowercase()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +292,7 @@ pub fn parse_slash_commands(content: &str) -> Vec<StatusCommand> {
|
||||
let s = s.as_str();
|
||||
let s = s.trim_start_matches('@');
|
||||
debug!("ADD ADMIN: {}", s);
|
||||
commands.push(StatusCommand::GrantAdmin(s.to_owned()));
|
||||
commands.push(StatusCommand::GrantAdmin(s.to_lowercase()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,7 +301,7 @@ pub fn parse_slash_commands(content: &str) -> Vec<StatusCommand> {
|
||||
let s = s.as_str();
|
||||
let s = s.trim_start_matches('@');
|
||||
debug!("REMOVE ADMIN: {}", s);
|
||||
commands.push(StatusCommand::RemoveAdmin(s.to_owned()));
|
||||
commands.push(StatusCommand::RemoveAdmin(s.to_lowercase()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,9 +46,17 @@ impl<'a> ProcessMention<'a> {
|
||||
Err(e.into())
|
||||
}
|
||||
Ok(Ok(res)) => {
|
||||
debug!("Result: {:#?}", res);
|
||||
|
||||
if let Some(item) = res.accounts.into_iter().next() {
|
||||
debug!("Search done, account found");
|
||||
Ok(Some(item.id))
|
||||
let acct_normalized = normalize_acct(&item.acct, &self.group_acct)?;
|
||||
if acct_normalized == acct {
|
||||
debug!("Search done, account found: {}", item.acct);
|
||||
Ok(Some(item.id))
|
||||
} else {
|
||||
warn!("Search done but found wrong account: {}", item.acct);
|
||||
Ok(None)
|
||||
}
|
||||
} else {
|
||||
debug!("Search done, nothing found");
|
||||
Ok(None)
|
||||
@@ -80,12 +88,14 @@ impl<'a> ProcessMention<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
async fn follow_user(&self, id: &str) -> Result<(), GroupError> {
|
||||
async fn follow_user_by_id(&self, id: &str) -> Result<(), GroupError> {
|
||||
debug!("Trying to follow user #{}", id);
|
||||
self.client.follow(id).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn unfollow_user(&self, id: &str) -> Result<(), GroupError> {
|
||||
async fn unfollow_user_by_id(&self, id: &str) -> Result<(), GroupError> {
|
||||
debug!("Trying to unfollow user #{}", id);
|
||||
self.client.unfollow(id).await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -373,13 +383,15 @@ impl<'a> ProcessMention<'a> {
|
||||
match self.config.set_member(&u, true) {
|
||||
Ok(_) => {
|
||||
self.add_reply(format!("User {} added to the group!", u));
|
||||
self.follow_user(&self.status_user_id)
|
||||
self.follow_by_acct(&u)
|
||||
.await.log_error("Failed to follow");
|
||||
}
|
||||
Err(e) => {
|
||||
self.add_reply(format!("Failed to add user {} to group: {}", u, e));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
debug!("User was already a member");
|
||||
}
|
||||
} else {
|
||||
self.add_reply("Only admins can manage members");
|
||||
@@ -505,49 +517,48 @@ impl<'a> ProcessMention<'a> {
|
||||
self.add_reply("This is a public-access group. ");
|
||||
}
|
||||
|
||||
if self.config.can_write(&self.status_acct) {
|
||||
if self.is_admin {
|
||||
self.add_reply("*You are an admin.*");
|
||||
} else {
|
||||
self.add_reply("*You are a member.*");
|
||||
}
|
||||
if self.is_admin {
|
||||
self.add_reply("*You are an admin.*");
|
||||
} else if self.config.is_member(&self.status_acct) {
|
||||
self.add_reply("*You are a member.*");
|
||||
} else if self.config.is_member_only() {
|
||||
self.add_reply("*You are not a member, ask one of the admins to add you.*");
|
||||
} else {
|
||||
if self.config.is_member_only() {
|
||||
self.add_reply("*You are not a member, ask one of the admins to add you.*");
|
||||
} else {
|
||||
self.add_reply("*You are not a member, follow or use /join to join the group.*");
|
||||
}
|
||||
self.add_reply("*You are not a member, follow or use /join to join the group.*");
|
||||
}
|
||||
|
||||
self.add_reply("\n\
|
||||
To share an original post, mention the group user.\n\
|
||||
To share a post, mention the group user or use one of the group hashtags. \
|
||||
Replies and mentions with commands won't be shared.\n\
|
||||
\n\
|
||||
**Supported commands:**\n\
|
||||
`/boost, /b` - boost the replied-to post into the group\n\
|
||||
`/ignore, /i` - make the group completely ignore the post\n\
|
||||
`/ping` - check that the service is alive\n\
|
||||
`/boost`, `/b` - boost the replied-to post into the group\n\
|
||||
`/ignore`, `/i` - make the group ignore the post\n\
|
||||
`/ping` - check the service is alive\n\
|
||||
`/tags` - show group hashtags\n\
|
||||
`/join` - join the group\n\
|
||||
`/leave` - leave the group");
|
||||
|
||||
if self.config.is_member_only() {
|
||||
self.add_reply("`/members, /who` - show group members / admins");
|
||||
self.add_reply("`/members`, `/who` - show group members / admins");
|
||||
} else {
|
||||
self.add_reply("`/members, /who` - show group admins");
|
||||
self.add_reply("`/members`, `/who` - show group admins");
|
||||
}
|
||||
|
||||
if self.is_admin {
|
||||
self.add_reply("\n\
|
||||
**Admin commands:**\n\
|
||||
`/add user` - add a member (use e-mail style address)\n\
|
||||
`/kick, /remove user` - kick a member\n\
|
||||
`/ban x` - ban a user or a server\n\
|
||||
`/remove user` - remove a member\n\
|
||||
`/add #hashtag` - add a group hashtag\n\
|
||||
`/remove #hashtag` - remove a group hashtag\n\
|
||||
`/ban x` - ban a user or server\n\
|
||||
`/unban x` - lift a ban\n\
|
||||
`/op, /admin user` - grant admin rights\n\
|
||||
`/deop, /deadmin user` - revoke admin rights\n\
|
||||
`/admin user` - grant admin rights\n\
|
||||
`/deadmin user` - revoke admin rights\n\
|
||||
`/opengroup` - make member-only\n\
|
||||
`/closegroup` - make public-access\n\
|
||||
`/announce x` - make a public announcement from the rest of the status");
|
||||
`/announce x` - make a public announcement from the rest of the status (without formatting)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -575,7 +586,7 @@ impl<'a> ProcessMention<'a> {
|
||||
// admin can leave but that's a bad idea
|
||||
let _ = self.config.set_member(&self.status_acct, false);
|
||||
self.add_reply("You're no longer a group member. Unfollow the group user to stop receiving group messages.");
|
||||
self.unfollow_user(&self.status_user_id).await
|
||||
self.unfollow_user_by_id(&self.status_user_id).await
|
||||
.log_error("Failed to unfollow");
|
||||
}
|
||||
}
|
||||
@@ -585,7 +596,7 @@ impl<'a> ProcessMention<'a> {
|
||||
debug!("Already member or admin, try to follow-back again");
|
||||
// Already a member, so let's try to follow the user
|
||||
// again, maybe first time it failed
|
||||
self.follow_user(&self.status_user_id).await
|
||||
self.follow_user_by_id(&self.status_user_id).await
|
||||
.log_error("Failed to follow");
|
||||
} else {
|
||||
// Not a member yet
|
||||
@@ -598,7 +609,7 @@ impl<'a> ProcessMention<'a> {
|
||||
self.append_admin_list_to_reply();
|
||||
} else {
|
||||
// Open access, try to follow back
|
||||
self.follow_user(&self.status_user_id).await
|
||||
self.follow_user_by_id(&self.status_user_id).await
|
||||
.log_error("Failed to follow");
|
||||
|
||||
// This only fails if the user is banned, but that is filtered above
|
||||
@@ -618,7 +629,15 @@ impl<'a> ProcessMention<'a> {
|
||||
async fn unfollow_by_acct(&self, acct: &str) -> Result<(), GroupError> {
|
||||
// Try to unfollow
|
||||
if let Ok(Some(id)) = self.lookup_acct_id(acct, true).await {
|
||||
self.unfollow_user(&id).await?;
|
||||
self.unfollow_user_by_id(&id).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn follow_by_acct(&self, acct: &str) -> Result<(), GroupError> {
|
||||
// Try to unfollow
|
||||
if let Ok(Some(id)) = self.lookup_acct_id(acct, false).await {
|
||||
self.follow_user_by_id(&id).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ use crate::error::GroupError;
|
||||
use crate::store::ConfigStore;
|
||||
use crate::store::data::GroupConfig;
|
||||
use crate::utils::{LogError, normalize_acct, VisExt};
|
||||
use crate::command::StatusCommand;
|
||||
|
||||
mod handle_mention;
|
||||
|
||||
@@ -241,7 +242,7 @@ impl GroupHandle {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle a non-mention status
|
||||
/// Handle a non-mention status for tags
|
||||
async fn handle_status(&mut self, s: Status) -> Result<(), GroupError> {
|
||||
debug!("Handling status #{}", s.id);
|
||||
let ts = s.timestamp_millis();
|
||||
@@ -252,11 +253,22 @@ impl GroupHandle {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if s.in_reply_to_id.is_some() {
|
||||
debug!("Status is a reply, discard");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if !s.content.contains('#') {
|
||||
debug!("No tags in status, discard");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let commands = crate::command::parse_slash_commands(&s.content);
|
||||
if commands.contains(&StatusCommand::Ignore) {
|
||||
debug!("Post has IGNORE command, discard");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let group_user = self.config.get_acct();
|
||||
let status_user = normalize_acct(&s.account.acct, group_user)?;
|
||||
|
||||
|
||||
@@ -82,7 +82,13 @@ async fn main() -> anyhow::Result<()> {
|
||||
.await?;
|
||||
|
||||
if let Some(handle) = args.value_of("auth") {
|
||||
let handle = handle.to_lowercase();
|
||||
let acct = handle.trim_start_matches('@');
|
||||
|
||||
if store.group_exists(acct).await {
|
||||
anyhow::bail!("Group already exists in config!");
|
||||
}
|
||||
|
||||
if let Some(server) = acct_to_server(acct) {
|
||||
let g = store
|
||||
.auth_new_group(NewGroupOptions {
|
||||
|
||||
+5
-4
@@ -152,6 +152,10 @@ impl ConfigStore {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub async fn group_exists(&self, acct : &str) -> bool {
|
||||
self.data.read().await.groups.contains_key(acct)
|
||||
}
|
||||
|
||||
/*
|
||||
pub(crate) async fn get_group_config(&self, group: &str) -> Option<GroupConfig> {
|
||||
let c = self.data.read().await;
|
||||
@@ -191,10 +195,7 @@ impl ConfigStore {
|
||||
}
|
||||
|
||||
fn make_scopes() -> Scopes {
|
||||
Scopes::read(scopes::Read::Accounts)
|
||||
| Scopes::read(scopes::Read::Notifications)
|
||||
| Scopes::read(scopes::Read::Statuses)
|
||||
| Scopes::read(scopes::Read::Follows)
|
||||
Scopes::read_all()
|
||||
| Scopes::write(scopes::Write::Statuses)
|
||||
| Scopes::write(scopes::Write::Media)
|
||||
| Scopes::write(scopes::Write::Follows)
|
||||
|
||||
+13
-6
@@ -1,4 +1,3 @@
|
||||
use std::borrow::Cow;
|
||||
use std::error::Error;
|
||||
|
||||
use elefren::status_builder::Visibility;
|
||||
@@ -24,14 +23,14 @@ pub(crate) fn acct_to_server(acct: &str) -> Option<&str> {
|
||||
acct.trim_start_matches('@').split('@').nth(1)
|
||||
}
|
||||
|
||||
pub(crate) fn normalize_acct<'a, 'g>(acct: &'a str, group: &'g str) -> Result<Cow<'a, str>, GroupError> {
|
||||
let acct = acct.trim_start_matches('@');
|
||||
if acct_to_server(acct).is_some() {
|
||||
pub(crate) fn normalize_acct(acct: &str, group: &str) -> Result<String, GroupError> {
|
||||
let acct = acct.trim_start_matches('@').to_lowercase();
|
||||
if acct_to_server(&acct).is_some() {
|
||||
// already has server
|
||||
Ok(Cow::Borrowed(acct))
|
||||
Ok(acct)
|
||||
} else if let Some(gs) = acct_to_server(group) {
|
||||
// attach server from the group actor
|
||||
Ok(Cow::Owned(format!("{}@{}", acct, gs)))
|
||||
Ok(format!("{}@{}", acct, gs))
|
||||
} else {
|
||||
Err(GroupError::BadConfig(
|
||||
format!("Group acct {} is missing server!", group).into(),
|
||||
@@ -81,6 +80,14 @@ mod test {
|
||||
Ok("piggo@piggo.space".into()),
|
||||
normalize_acct("piggo@piggo.space", "uhh")
|
||||
);
|
||||
assert_eq!(
|
||||
Ok("piggo@piggo.space".into()),
|
||||
normalize_acct("piGGgo@pIggo.spaCe", "uhh")
|
||||
);
|
||||
assert_eq!(
|
||||
Ok("piggo@banana.nana".into()),
|
||||
normalize_acct("piGGgo", "foo@baNANA.nana")
|
||||
);
|
||||
assert_eq!(Err(GroupError::BadConfig("_".into())), normalize_acct("piggo", "uhh"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user