Handle expected failures, collect replies and limit concurrent reads
After the first Effect bot, these examples take a native Client as a parameter. It can come from create, from the Fluxer service described in Effect application testing or from the client that run passes to its handlers and setup. Each helper returns an Effect. Call it with yield* inside the application program
Handle Success and Expected Failure
Use Effect to turn either success or an expected failure into a value. For example, a status panel can report whether a notice was sent without storing its content or a raw error
import { Effect } from "effect"
import type { Client } from "@neontechspace/fluxerly/effect"
export function sendNotice(client: Client, channelId: string) {
return client.messages.send(channelId, "The event starts in ten minutes").pipe(
Effect.match({
onSuccess: message => ({ status: "confirmed" as const, messageId: message.id }),
onFailure: error => ({ status: "failed" as const, error }),
}),
)
}
The failure branch retains the expected SDK error for the caller. Defects and interruption still propagate to the caller
Decide Whether a Failed Send Can Be Repeated
A message error's outcome field is not when the request never left the client, rejected when Fluxer refused it, and unknown when it may have been delivered. Reconcile an unknown outcome before another send attempt, because repeating it can post a duplicate. The errors helper, exported by both entry points, says whether repeating the same call is safe. See reliability
Collect Replies and Clean Up
A nested scope cleans up a short conversation's collector while the bot stays open
import { Effect } from "effect"
import type { Client } from "@neontechspace/fluxerly/effect"
export function askPreferredName(client: Client, channelId: string, userId: string) {
return Effect.scoped(
Effect.gen(function* () {
const collector = yield* client.messages.collect(channelId, {
filter: message => message.author.id === userId && !message.author.isBot,
maxMessages: 1,
timeoutMs: 30_000,
})
yield* client.messages.send(channelId, "What name should the bot use?")
const result = yield* collector.result()
return result.messages[0]?.content
}),
)
}
The client must already be connected. The yield* expression finishes registration before sending the prompt, so a fast reply can be observed. Success, prompt failure and interruption all release the collector before this helper completes. The outer client stays open
A timeout can return undefined. A gateway gap fails collection because replies may have been missed. The prompt remains posted after collection ends
Run Independent Reads Together
Fetch independent data at the same time, with a limit on concurrent requests. This example fetches a community and its channels without opening the gateway
import { Effect } from "effect"
import type { Client } from "@neontechspace/fluxerly/effect"
export function readGuildOverview(client: Client, guildId: string) {
return Effect.all({
guild: client.guilds.fetch(guildId),
channels: client.channels.fetchAll(guildId),
}, { concurrency: 2 })
}
On success, the result has guild and channels properties. A failure interrupts unfinished sibling work and waits for its finalizers. The SDK's request limits and rate-limit handling still apply. Concurrent reads can observe different remote revisions
Count Messages Without Storing Them All
A Stream describes values processed as they arrive. A remote scan can run without collecting every result in an array
import { Stream } from "effect"
import type { Client } from "@neontechspace/fluxerly/effect"
export function countRecentHumanMessages(client: Client, channelId: string) {
return client.messages.iterateHistory(channelId, { maxItems: 200, pageSize: 50 }).pipe(
Stream.runFold(() => 0, (count, message) => count + (message.author.isBot ? 0 : 1)),
)
}
The returned Effect counts human-authored observations among at most 200 messages, newest first. Requests begin when the Stream is consumed, page failures remain failures, and interruption waits for in-flight cleanup
Read History and Post a Summary
Keep reusable work as Effects and run the completed program at the application entry point. This example creates one scoped client, reads at most 200 messages, sends a summary and then closes the client
import { Effect, Stream } from "effect"
import { createClient } from "@neontechspace/fluxerly/effect"
export function postActivitySummary(token: string, channelId: string) {
return Effect.scoped(
Effect.gen(function* () {
const client = yield* createClient({ token })
const count = yield* client.messages.iterateHistory(channelId, {
maxItems: 200,
pageSize: 50,
}).pipe(Stream.runFold(() => 0, total => total + 1))
return yield* client.messages.send(channelId, `Scanned ${count} recent messages`)
}),
)
}
Run the returned Effect with Effect from the application entry point, or yield* it inside a larger program. A failed history scan skips the summary. Reconcile an uncertain summary delivery before rerunning the job, which can otherwise post a duplicate
For the next step, learn Effect resource management and expected errors versus defects. Use services and Layers to supply shared application dependencies