### Basic Slack Bot Implementation Source: https://github.com/laserdisc-io/slack4s/blob/main/README.md A minimal example of a Slack bot using slack4s. It sets up a SlashCommandBotBuilder with a signing secret and starts serving requests. The signing secret should be kept secure and not hardcoded in production. ```scala import cats.effect.{IO, IOApp} import io.laserdisc.slack4s.slashcmd.* object MySlackBot extends IOApp.Simple { val secret: SigningSecret = SigningSecret.unsafeFrom("your-signing-secret") // demo purposes - please don't hardcode secrets override def run: IO[Unit] = SlashCommandBotBuilder[IO](secret).serve } ``` -------------------------------- ### Slack4s Service Startup Logs Source: https://github.com/laserdisc-io/slack4s/blob/main/docs/tutorial.md Example log output from a slack4s service startup. It shows the service binding to a port and the http4s version information. This output is useful for verifying the service is running correctly. ```log 2021-09-16 22:56:39,920 INFO o.h.b.c.n.NIO1SocketServerGroup [io-compute-6] Service bound to address /0:0:0:0:0:0:0:0:8080 2021-09-16 22:56:39,926 INFO o.h.b.s.BlazeServerBuilder [io-compute-6] ---------------------------------------------------------- Starting slack4s v0.0.0+21-40742d0b+20210910-2234-SNAPSHOT ---------------------------------------------------------- 2021-09-16 22:56:39,945 INFO o.h.b.s.BlazeServerBuilder [io-compute-6] http4s v0.23.3 on blaze v0.15.2 started at http://[::]:8080/ ``` -------------------------------- ### Space News Slash Command Example Source: https://github.com/laserdisc-io/slack4s/blob/main/docs/tutorial.md This example demonstrates the usage of the `/spacenews` slash command in Slack, taking a search term as an argument. It shows a typical interaction flow. ```scala // Example usage within a Slack command handler (conceptual) // Assuming 'query' is extracted from the Slack command text val searchTerm = "nasa" val apiUrl = s"https://api.spaceflightnewsapi.net/v3/articles?_limit=3&title_contains=$searchTerm" // Code to fetch data from apiUrl and format the response would follow... ``` -------------------------------- ### Slack Bot Implementation in Scala Source: https://github.com/laserdisc-io/slack4s/blob/main/docs/tutorial.md This Scala code demonstrates how to create a basic Slack bot using the slack4s library. It initializes the bot with a signing secret and starts the service. Ensure you have a logging implementation like logback on the classpath for log output. ```scala import cats.effect.{IO, IOApp} import io.laserdisc.slack4s.slashcmd.* object MySlackBot extends IOApp.Simple { // please don't hardcode secrets, this is just a demo val secret: SigningSecret = SigningSecret.unsfeFrom("7e16-----redacted------68c2c") override def run: IO[Unit] = SlashCommandBotBuilder[IO](secret).serve } ``` -------------------------------- ### ngrok Tunnel Setup Source: https://github.com/laserdisc-io/slack4s/blob/main/docs/tutorial.md This snippet shows how to set up an ngrok tunnel to forward traffic to a local service running on port 8080. It's crucial for exposing the local development service to Slack. ```bash ❯ ngrok http 8080 Session Status online Web Interface http://127.0.0.1:4040 Forwarding http://2846-173-61-91-146.ngrok.io -> http://localhost:8080 Forwarding https://2846-173-61-91-146.ngrok.io -> http://localhost:8080 ``` -------------------------------- ### Slack Command Bot Builder Integration Source: https://github.com/laserdisc-io/slack4s/blob/main/docs/tutorial.md Demonstrates how to integrate the custom command mapper with the SlashCommandBotBuilder. This involves providing the Slack bot secret and the mapper function to the builder, and then starting the server to handle incoming Slack commands. ```scala import io.laserdisc.slack4s.slashcmd.builder.SlashCommandBotBuilder import cats.effect.IO // Assuming 'secret' is defined and 'mapper' is the CommandMapper function // val secret: String = "YOUR_SLACK_BOT_SECRET" // SlashCommandBotBuilder[IO](secret) // .withCommandMapper(mapper) // .serve ``` -------------------------------- ### Slack Slash Command Request Example Source: https://github.com/laserdisc-io/slack4s/blob/main/docs/tutorial.md This snippet shows the HTTP request made by Slack to the configured ngrok URL when a slash command is invoked. It indicates a '502 Bad Gateway' error, suggesting the local service was not yet running or properly configured. ```APIDOC HTTP Requests ------------- POST /slack/slashCmd 502 Bad Gateway ``` -------------------------------- ### Space News API Endpoint Source: https://github.com/laserdisc-io/slack4s/blob/main/docs/tutorial.md This is the GET endpoint for the Spaceflight News API used to query articles. It allows filtering by title and limiting the number of results. ```APIDOC GET https://api.spaceflightnewsapi.net/v3/articles?_limit=3&title_contains=nasa Description: Queries space news articles. Parameters: _limit: Maximum number of articles to return. title_contains: Filters articles where the title contains the specified keyword. ``` -------------------------------- ### Query Space News API Source: https://github.com/laserdisc-io/slack4s/blob/main/docs/tutorial.md Performs a GET request to the Spaceflight News API to retrieve articles based on a search term. It uses http4s for the client and circe for decoding the JSON response into a list of SpaceNewsArticle objects. The query is limited to 3 results and filters by title. ```scala import io.circe.generic.auto.* import org.http4s.Method.GET import org.http4s.Uri.unsafeFromString import org.http4s.* import org.http4s.blaze.client.BlazeClientBuilder import org.http4s.circe.CirceEntityCodec.circeEntityDecoder import java.time.Instant import cats.effect.IO import scala.concurrent.ExecutionContext.global // our simple model for representing the result case class SpaceNewsArticle( title: String, url: String, imageUrl: String, newsSite: String, summary: String, updatedAt: Instant ) // perform a GET on the API, deocding the results into a list of our model above def querySpaceNews(word: String): IO[List[SpaceNewsArticle]] = BlazeClientBuilder[IO](global).resource.use { // will create a client for each invocation, but this is just a demo _.fetchAs[List[SpaceNewsArticle]]( Request[IO]( GET, unsafeFromString(s"https://api.spaceflightnewsapi.net/v3/articles") .withQueryParam("_limit", "3") .withQueryParam("title_contains", word) ) ) } ``` -------------------------------- ### Slack App Manifest Configuration Source: https://github.com/laserdisc-io/slack4s/blob/main/docs/tutorial.md This YAML manifest defines the configuration for a Slack application, including its display information, features like slash commands, and OAuth scopes. The `url` parameter must be updated with the ngrok forwarding address. ```yaml _metadata: major_version: 1 minor_version: 1 display_information: name: Space News! description: Space News App background_color: "#080f06" features: bot_user: display_name: spacenews always_online: false slash_commands: - command: /spacenews url: https://2846-173-61-91-146.ngrok.io/slack/slashCmd description: Query space news by topic keywords usage_hint: nasa should_escape: false oauth_config: scopes: bot: - commands settings: org_deploy_enabled: false socket_mode_enabled: false token_rotation_enabled: false ``` -------------------------------- ### Format News Article for Slack Source: https://github.com/laserdisc-io/slack4s/blob/main/docs/tutorial.md Helper function to format a single SpaceNewsArticle into Slack's Block Kit layout. It creates a sequence of LayoutBlock objects, including a markdown section with an image, a context section with metadata, and a divider. ```scala import io.laserdisc.slack4s.slack.* import io.laserdisc.slack4s.slack.models.LayoutBlock import io.laserdisc.slack4s.slack.models.URL def formatNewsArticle(article: SpaceNewsArticle): Seq[LayoutBlock] = Seq( markdownWithImgSection( markdown = s"*<${article.url}|${article.title}>*\n${article.summary}", imageUrl = URL.unsafeFrom(article.imageUrl), imageAlt = s"Image for ${article.title}" ), contextSection( markdownElement(s"*Via ${article.newsSite}* - _last updated: ${article.updatedAt}_") ), dividerSection ) ``` -------------------------------- ### Customizing Slack Bot Configuration Source: https://github.com/laserdisc-io/slack4s/blob/main/README.md Demonstrates how to customize the Slack bot's behavior using the SlashCommandBotBuilder. This includes setting a custom command mapper, specifying bind options (port and address), and customizing the underlying http4s BlazeServerBuilder. ```scala SlashCommandBotBuilder[IO](secret) .withCommandMapper(testCommandMapper) // your mapper impl, see next section .withBindOptions(port = 9999, address = "192.168.0.1") // by default, binds to 0.0.0.0:8080 .withHttp4sBuilder{ // offer the chance to customize http4s' BlazeServerBuilder used under the hood // USE WITH CAUTION; it overrides any settings set by slack4s _.withIdleTimeout(10.seconds) .withMaxConnections(512) } .serve ``` -------------------------------- ### Slack Command Mapper Implementation Source: https://github.com/laserdisc-io/slack4s/blob/main/docs/tutorial.md Implements the CommandMapper for Slack4s, defining how to translate a SlashCommandPayload into a Command. It handles empty input by returning an error message and processes search terms by calling the API and formatting results. Responses are marked as Delayed to accommodate potential API latency. ```scala import io.laserdisc.slack4s.slashcmd.CommandMapper import io.laserdisc.slack4s.slashcmd.models.SlashCommandPayload import io.laserdisc.slack4s.slashcmd.models.Command import io.laserdisc.slack4s.slashcmd.models.ResponseType.Delayed import io.laserdisc.slack4s.slashcmd.models.ResponseType.Immediate import io.laserdisc.slack4s.slack.models.slackMessage import io.laserdisc.slack4s.slack.models.headerSection import cats.effect.IO // Assuming querySpaceNews and formatNewsArticle are defined elsewhere def mapper: CommandMapper[IO] = { (payload: SlashCommandPayload) => payload.getText.trim match { case "" => Command( handler = IO.pure(slackMessage(headerSection("Please provide a search term!"))), responseType = Immediate ) case searchTerm => Command( handler = querySpaceNews(searchTerm).map { case Seq() => slackMessage( headerSection(s"No results for: $searchTerm") ) case articles => slackMessage( headerSection(s"Space news results for: $searchTerm") +: articles.flatMap(formatNewsArticle) ) }, responseType = Delayed ) } } ``` -------------------------------- ### Slack4s Command Structure Source: https://github.com/laserdisc-io/slack4s/blob/main/README.md Defines the structure for handling Slack commands, including the response effect, response type, and logging. ```APIDOC Command[F]: handler: F[ChatPostMessageRequest] The effect to evaluate in response to the input. ChatPostMessageRequest is the Slack API Java model representing the response message. Importing io.laserdisc.slack4s.slack._ provides convenience functions like slackMessage(...) and markdownWithImgSection(...). Explore response structures with the Slack Block Kit Builder. responseType: ResponseType Determines whether to respond immediately or process in a background queue and post to a callback URL. Refer to the scaladoc for ResponseType for all options. logId: LogToken Defaults to "NA". This token is used in slack4s's logs for processing this command, useful for log filtering. ``` -------------------------------- ### Implementing CommandMapper Source: https://github.com/laserdisc-io/slack4s/blob/main/README.md Shows how to implement and provide a custom CommandMapper to the SlashCommandBotBuilder. The mapper's implementation determines how the bot responds to incoming slash commands after signature validation. ```scala val myMapper: CommandMapper[F] = .. // see the next section SlashCommandBotBuilder[IO](secret) .withCommandMapper(myMapper) .serve ``` -------------------------------- ### Add slack4s Dependency Source: https://github.com/laserdisc-io/slack4s/blob/main/README.md This snippet shows how to add the slack4s library as a dependency in your sbt build file. Replace 'latestVersion' with the actual latest version number. ```sbt libraryDependencies += "io.laserdisc" %% "slack4s" % latestVersion ``` -------------------------------- ### CommandMapper Type Alias Source: https://github.com/laserdisc-io/slack4s/blob/main/README.md Defines the type alias for CommandMapper, which is a function that takes a SlashCommandPayload and returns an effectful Command. This is where your bot's business logic resides. ```scala SlashCommandPayload => F[Command[F]] ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.