<commit_before>(ns yetibot.core.adapters.slack
  (:require
    [clojure.core.async :as async]
    [clojure.pprint :refer [pprint]]
    [clojure.core.memoize :as memo]
    [yetibot.core.adapters.adapter :as a]
    [robert.bruce :refer [try-try-again] :as rb]
    [gniazdo.core :as ws]
    [clojure.string :as s]
    [schema.core :as sch]
    [yetibot.core.interpreter :refer [*chat-source*]]
    [yetibot.core.models.users :as users]
    [yetibot.core.util.http :refer [html-decode]]
    [clj-slack
     [users :as slack-users]
     [im :as im]
     [chat :as slack-chat]
     [channels :as channels]
     [groups :as groups]
     [reactions :as reactions]
     [conversations :as conversations]
     [rtm :as rtm]]
    [slack-rtm.core :as slack]
    [taoensso.timbre :as timbre :refer [color-str debug info warn error]]
    [yetibot.core.handler :refer [handle-raw]]
    [yetibot.core.chat :refer [base-chat-source chat-source
                               chat-data-structure *thread-ts* *target* *adapter*]]
    [yetibot.core.util :as util :refer [image?]]))

(def channel-cache-ttl 60000)

(def slack-ping-pong-interval-ms
  "How often to send Slack a ping event to ensure the connection is active"
  10000)

(def slack-ping-pong-timeout-ms
  "How long to wait for a `pong` after sending a `ping` before marking the
   connection as inactive and attempting to restart it"
  5000)

(defn slack-config
  "Transforms yetibot config to expected Slack config"
  [config]
  {:api-url (or (:endpoint config) "https://slack.com/api")
   :token (:token config)})

(defn list-channels [config] (channels/list (slack-config config)))

(def channels-cached
  (memo/ttl
    (comp :channels list-channels)
    :ttl/threshold channel-cache-ttl))

(defn channel-by-id [id config]
  (first (filter #(= id (:id %)) (channels-cached config))))

(defn list-groups [config] (groups/list (slack-config config)))

(defn channels-in
  "all channels that yetibot is a member of"
  [config]
  (filter :is_member (:channels (list-channels config))))

(defn self
  "Slack acount for yetibot from `rtm.start` (represented at (-> @conn :start)).
   You must call `start` in order to define `conn`."
  [conn]
  (-> @conn :start :self))

(defn find-yetibot-user
  [conn cs]
  (let [yetibot-uid (:id (self conn))]
    (users/get-user cs yetibot-uid)))

;;;;

(defn chan-or-group-name
  "Takes either a channel or a group and returns its name as a String.
   If it's a public channel, # is prepended.
   If it's a group, just return the name."
  [chan-or-grp]
  (str (when (:is_channel chan-or-grp) "#")
       (:name chan-or-grp)))

(defn channels
  "A vector of channels and any private groups by name"
  [config]
  (concat
    (map :name (:groups (list-groups config)))
    (map #(str "#" (:name %)) (channels-in config))))

(defn send-msg [config msg]
  (let [img? (image? msg)
        _ (debug "send-msg"
                 (color-str :blue (pr-str config))
                 {:img? img?
                  :target *target*
                  :thread-ts *thread-ts*})

        {:keys [ok error response_metadata] :as response}
        (slack-chat/post-message
          (slack-config config) *target* msg
          (merge
            {:unfurl_media "true" :as_user "true"}
            (when img?
              {:blocks [{"type" "image"
                         "image_url" msg
                         "alt_text" msg}]})
            (when *thread-ts*
              {:thread_ts *thread-ts*})))]
    (if ok
      (debug "slack response" (pr-str response))
      (error "error posting to slack" (pr-str response)))))<commit_msg>Fixup error being shadowed by destructure in Slack<commit_after>(ns yetibot.core.adapters.slack
  (:require
    [clojure.core.async :as async]
    [clojure.pprint :refer [pprint]]
    [clojure.core.memoize :as memo]
    [yetibot.core.adapters.adapter :as a]
    [robert.bruce :refer [try-try-again] :as rb]
    [gniazdo.core :as ws]
    [clojure.string :as s]
    [schema.core :as sch]
    [yetibot.core.interpreter :refer [*chat-source*]]
    [yetibot.core.models.users :as users]
    [yetibot.core.util.http :refer [html-decode]]
    [clj-slack
     [users :as slack-users]
     [im :as im]
     [chat :as slack-chat]
     [channels :as channels]
     [groups :as groups]
     [reactions :as reactions]
     [conversations :as conversations]
     [rtm :as rtm]]
    [slack-rtm.core :as slack]
    [taoensso.timbre :as timbre :refer [color-str debug info warn error]]
    [yetibot.core.handler :refer [handle-raw]]
    [yetibot.core.chat :refer [base-chat-source chat-source
                               chat-data-structure *thread-ts* *target* *adapter*]]
    [yetibot.core.util :as util :refer [image?]]))

(def channel-cache-ttl 60000)

(def slack-ping-pong-interval-ms
  "How often to send Slack a ping event to ensure the connection is active"
  10000)

(def slack-ping-pong-timeout-ms
  "How long to wait for a `pong` after sending a `ping` before marking the
   connection as inactive and attempting to restart it"
  5000)

(defn slack-config
  "Transforms yetibot config to expected Slack config"
  [config]
  {:api-url (or (:endpoint config) "https://slack.com/api")
   :token (:token config)})

(defn list-channels [config] (channels/list (slack-config config)))

(def channels-cached
  (memo/ttl
    (comp :channels list-channels)
    :ttl/threshold channel-cache-ttl))

(defn channel-by-id [id config]
  (first (filter #(= id (:id %)) (channels-cached config))))

(defn list-groups [config] (groups/list (slack-config config)))

(defn channels-in
  "all channels that yetibot is a member of"
  [config]
  (filter :is_member (:channels (list-channels config))))

(defn self
  "Slack acount for yetibot from `rtm.start` (represented at (-> @conn :start)).
   You must call `start` in order to define `conn`."
  [conn]
  (-> @conn :start :self))

(defn find-yetibot-user
  [conn cs]
  (let [yetibot-uid (:id (self conn))]
    (users/get-user cs yetibot-uid)))

;;;;

(defn chan-or-group-name
  "Takes either a channel or a group and returns its name as a String.
   If it's a public channel, # is prepended.
   If it's a group, just return the name."
  [chan-or-grp]
  (str (when (:is_channel chan-or-grp) "#")
       (:name chan-or-grp)))

(defn channels
  "A vector of channels and any private groups by name"
  [config]
  (concat
    (map :name (:groups (list-groups config)))
    (map #(str "#" (:name %)) (channels-in config))))

(defn send-msg [config msg]
  (let [img? (image? msg)
        _ (debug "send-msg"
                 (color-str :blue (pr-str config))
                 {:img? img?
                  :target *target*
                  :thread-ts *thread-ts*})

        {:keys [ok response_metadata]
         slack-error :error :as response}
        (slack-chat/post-message
          (slack-config config) *target* msg
          (merge
            {:unfurl_media "true" :as_user "true"}
            (when img?
              {:blocks [{"type" "image"
                         "image_url" msg
                         "alt_text" "Image"}]})
            (when *thread-ts*
              {:thread_ts *thread-ts*})))]
    (if ok
      (debug "slack response" (pr-str response))
      (error "error posting to slack" (pr-str response)))))
{'fraction_non_alphanumeric': 0.14201020267475528, 'fraction_numerical': 0.0038604715290224733, 'mean_word_length': 2.661786976274609, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1866786', 'n_tokens_mistral': 2589, 'n_tokens_neox': 2494, 'n_words': 731}