Skip to main content

ProdSlackFacade

Struct ProdSlackFacade 

Source
pub(crate) struct ProdSlackFacade {
    workspace: Weak<Workspace>,
}
Expand description

Production facade over Weak<Workspace> (weak to avoid a cycle with the MCP server the workspace owns).

Fields§

§workspace: Weak<Workspace>

Implementations§

Source§

impl ProdSlackFacade

Source

const THREAD_EXCERPT_CHARS: usize = 80

How much of a thread’s parent the card’s line quotes.

Source

pub(crate) fn from_arc(workspace: &Arc<Workspace>) -> Arc<dyn SlackFacade> ⓘ

Source

fn resolve( &self, caller: &SessionSlot, ) -> Result<(String, Arc<dyn SlackApi>), SlackWorkspaceError>

The workspace a tool call acts on, with the refusal shape every Slack tool shares.

Source

async fn resolve_conversation( api: &Arc<dyn SlackApi>, workspace: &str, conversation: &str, ) -> (String, String)

The conversation a request addresses, as (id, display label). conversation may be an id, a channel name or a DM’s partner, and the id is what the ledger and the in-flight mark key on - a post or an edit addressed one way lands in the same conversation the sweep reads. The label is what the approval prompt shows: a channel’s name carries the # the card draws, a DM is named after its partner - the partner’s id when Slack names none. A failed lookup is not a reason to refuse the action - but it IS why the prompt would show an id, so it says so.

Source

async fn name_draft( ws: &Arc<Workspace>, api: &Arc<dyn SlackApi>, label: &str, conversation: &str, draft: &mut SlackDraft, )

Name the draft’s thread and the people its text tags, at the moment it is parked. Never fails the park: whatever resolves rides the draft, and what does not stays unset.

Source

fn look_up_user( host: &Arc<SlackSubsystemHost>, api: &Arc<dyn SlackApi>, label: &str, user: String, ) -> JoinHandle<Option<String>> ⓘ

One user-name lookup on its own task, so a batch of them overlaps the thread read in front of the park.

Source

fn thread_name( parent: &SlackHistoryMessage, names: &HashMap<String, String>, ) -> Option<SlackThreadName>

The thread name drawn for parent: its author and one line of its text flattened to prose. None when the parent names no author at all.

Source

async fn await_approval( workspace: &Arc<Workspace>, caller: &SessionSlot, draft: SlackDraft, ) -> GateDecision

Register a draft for the user’s decision and wait for it. The one place “did the user approve this” is decided, so the fail-closed path exists once rather than at each gated operation - posting, replacing a body and deleting one all come through here.

No timeout rides the wait. A held post is an ask like a question, a permission or a hand-off: it parks until the reader answers or the asking session goes away, and the dock’s own “don’t send” is the way out. A clock here would refuse a draft the user was about to approve.

The views hear SlackDraftResolved from the answering dispatch: the draft leaving the registry is the whole of what a view does with it, and every path that removes it emits that one signal.

Trait Implementations§

Source§

impl SlackFacade for ProdSlackFacade

Source§

fn conversations<'life0, 'life1, 'async_trait>( &'life0 self, caller: &'life1 SessionSlot, ) -> Pin<Box<dyn Future<Output = Result<(String, Vec<SlackConversation>), SlackReadError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Every conversation the caller’s workspace token is in, with the label it came from - the one resolution the tool’s result names, so a reload between calls cannot report a different workspace than the data was read from.
Source§

fn post<'life0, 'life1, 'async_trait>( &'life0 self, caller: &'life1 SessionSlot, request: SlackPostRequest, ) -> Pin<Box<dyn Future<Output = Result<SlackPostOutcome, SlackPostError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Post a message, held for the user’s decision first. Returns only once that decision is in, and nothing is sent unless it was an approval - a rejected or unanswered draft is Rejected.
Source§

fn edit<'life0, 'life1, 'async_trait>( &'life0 self, caller: &'life1 SessionSlot, request: SlackEditRequest, ) -> Pin<Box<dyn Future<Output = Result<(), SlackEditError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Replace the body of one of the user’s own messages, or delete it when text is None. Read more
Source§

fn react<'life0, 'life1, 'async_trait>( &'life0 self, caller: &'life1 SessionSlot, request: SlackReactRequest, ) -> Pin<Box<dyn Future<Output = Result<(), SlackReactError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Add or remove one reaction, held for the user’s decision first - a reaction is authored content in the user’s name.
Source§

fn search<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, caller: &'life1 SessionSlot, query: &'life2 str, count: u32, conversation: Option<&'life3 str>, ) -> Pin<Box<dyn Future<Output = Result<(String, Vec<SlackSearchMatch>), SlackReadError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

search.messages in a workspace, always timestamp-ordered. It matches text; it cannot find every message that mentions a user. conversation keeps only hits from that conversation id.
Source§

fn history<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, caller: &'life1 SessionSlot, conversation: &'life2 str, limit: u32, oldest: Option<&'life3 str>, newest: Option<&'life4 str>, ) -> Pin<Box<dyn Future<Output = Result<(String, Vec<SlackHistoryMessage>), SlackReadError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait,

One page of a conversation’s messages. oldest and newest are exclusive bounds, so a caller pages backwards by feeding the earliest ts it saw back as newest.
Source§

fn replies<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, caller: &'life1 SessionSlot, conversation: &'life2 str, ts: &'life3 str, limit: u32, ) -> Pin<Box<dyn Future<Output = Result<(String, Vec<SlackHistoryMessage>), SlackReadError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Every reply in one thread, the parent first, up to limit rows.
Source§

fn user<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, caller: &'life1 SessionSlot, user: &'life2 str, ) -> Pin<Box<dyn Future<Output = Result<(String, SlackUser), SlackReadError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

users.info for one user id.
Source§

fn users<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, caller: &'life1 SessionSlot, query: &'life2 str, ) -> Pin<Box<dyn Future<Output = Result<(String, Vec<SlackUser>), SlackReadError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Every member whose handle, real name or display name contains query, case-insensitively. Every match is returned: silently picking one of several is how a mention reaches the wrong person. Deactivated members are excluded.
Source§

fn me<'life0, 'life1, 'async_trait>( &'life0 self, caller: &'life1 SessionSlot, ) -> Pin<Box<dyn Future<Output = Result<(String, AuthTest), SlackReadError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

auth.test: the resolved [[slack]] label and the identity the workspace’s token acts as. The label is returned alongside because it can differ from the team’s name (Subspace is team Autonomys).
Source§

fn pins<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, caller: &'life1 SessionSlot, conversation: &'life2 str, ) -> Pin<Box<dyn Future<Output = Result<(String, Vec<SlackPin>), SlackReadError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

pins.list for one conversation.
Source§

fn bookmarks<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, caller: &'life1 SessionSlot, conversation: &'life2 str, ) -> Pin<Box<dyn Future<Output = Result<(String, Vec<SlackBookmark>), SlackReadError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

bookmarks.list for one conversation.
Source§

fn fetch_attachment<'life0, 'life1, 'async_trait>( &'life0 self, caller: &'life1 SessionSlot, request: SlackFetchRequest, ) -> Pin<Box<dyn Future<Output = Result<PathBuf, SlackAttachmentError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Fetch one Slack file to request.dir and return where it landed. Not gated: reading a file is not posting one, and the name is sanitised so it cannot escape the directory.
Source§

fn post_attachment<'life0, 'life1, 'async_trait>( &'life0 self, caller: &'life1 SessionSlot, request: SlackUploadRequest, ) -> Pin<Box<dyn Future<Output = Result<(), SlackAttachmentError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Upload a local file into a conversation, held for the user’s decision first - an upload posts new content, so it is a first send and belongs behind the same gate as slack__post.
Source§

fn subscribed_targets( &self, caller: &SessionSlot, label: &str, ) -> Vec<SlackSubscription>

The caller’s OWN subscriptions in label - a lead’s the lead’s, a worker’s its own, never another owner’s. slack__list passes the label its read resolved, so the mark and the rows cannot disagree about which workspace they are in. After a restart these ids are the only handle a session has to unsubscribe with.
Source§

fn subscribe( &self, caller: &SessionSlot, request: SlackSubscribeRequest, ) -> Result<Vec<SlackSubscription>, SlackSubscribeError>

Record what the caller wants to watch in its project’s workspace, one record per target. Returns the records as they now stand, so a caller can name what it created.
Source§

fn unsubscribe( &self, caller: &SessionSlot, id: Uuid, ) -> Result<SlackSubscription, SlackUnsubscribeError>

Remove one of the caller’s OWN subscriptions by id within its project, returning the row as it stood. A refusal says which case it is: SlackUnsubscribeError::NoSuchSubscription when the project has no such id, SlackUnsubscribeError::NotOwnedByCaller when it has one under another owner.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

impl<T> Any for T
where T: Any,

§

fn into_any(self: Box<T>) -> Box<dyn Any>

§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

§

fn type_name(&self) -> &'static str

§

impl<T> AnySync for T
where T: Any + Send + Sync,

§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send> ⓘ

Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<S, T> Duplex<S> for T
where T: FromSample<S> + ToSample<S>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<S> FromSample<S> for S

§

fn from_sample_(s: S) -> S

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
§

impl<T, U> ToSample<U> for T
where U: FromSample<T>,

§

fn to_sample_(self) -> U

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more