<commit_before>module Amber
  module WebSockets
    # `ClientSocket` struct maps a user to an [HTTP::WebSocket](https://crystal-lang.org/api/0.22.0/HTTP/WebSocket.html).  For every websocket connection
    # there will be an associated ClientSocket.  Authentication and authorization happen within the `ClientSocket`.  `ClientSocket` will subscribe to `Channel`s,
    # where incoming and outgoing messages are routed through.
    #
    # Example:
    #
    # ```crystal
    # struct UserSocket < Amber::Websockets::ClientSocket
    #   channel "user_channel:*", UserChannel
    #   channel "room_channel:*", RoomChannel
    #
    #   def on_connect
    #     return some_auth_method!
    #   end
    # end
    # ```
    abstract struct ClientSocket
      @@channels = [] of NamedTuple(path: String, channel: Channel)

      MAX_SOCKET_IDLE_TIME = 100.seconds
      BEAT_INTERVAL        = 30.seconds

      protected getter id : String
      getter socket : HTTP::WebSocket
      protected getter context : HTTP::Server::Context
      protected getter raw_params : Amber::Router::Params
      protected getter params : Amber::Validators::Params
      protected getter session : Amber::Router::Session::AbstractStore?
      protected getter cookies : Amber::Router::Cookies::Store?
      private property pongs = Array(Time).new
      private property pings = Array(Time).new

      # Add a channel for this socket to listen, publish to
      def self.channel(channel_path, ch)
        @@channels.push({path: channel_path, channel: ch.new(WebSockets.topic_path(channel_path))})
      end

      def self.channels
        @@channels
      end

      def self.get_topic_channel(topic_path)
        topic_channels = @@channels.select { |ch| WebSockets.topic_path(ch[:path]) == topic_path }
        return topic_channels[0][:channel] if topic_channels.any?
      end

      # Broadcast a message to all subscribers of the topic
      #
      # ```crystal
      # UserSocket.broadcast("message", "chats_room:1", "msg:new", {"message" => "test"})
      # ```
      protected def self.broadcast(event : String, topic : String, subject : String, payload : Hash)
        if channel = get_topic_channel(WebSockets.topic_path(topic))
          channel.rebroadcast!({
            "event"   => event,
            "topic"   => topic,
            "subject" => subject,
            "payload" => payload,
          })
        end
      end<commit_msg>remove protected from ClientSocket.broadcast (#542)<commit_after>module Amber
  module WebSockets
    # `ClientSocket` struct maps a user to an [HTTP::WebSocket](https://crystal-lang.org/api/0.22.0/HTTP/WebSocket.html).  For every websocket connection
    # there will be an associated ClientSocket.  Authentication and authorization happen within the `ClientSocket`.  `ClientSocket` will subscribe to `Channel`s,
    # where incoming and outgoing messages are routed through.
    #
    # Example:
    #
    # ```crystal
    # struct UserSocket < Amber::Websockets::ClientSocket
    #   channel "user_channel:*", UserChannel
    #   channel "room_channel:*", RoomChannel
    #
    #   def on_connect
    #     return some_auth_method!
    #   end
    # end
    # ```
    abstract struct ClientSocket
      @@channels = [] of NamedTuple(path: String, channel: Channel)

      MAX_SOCKET_IDLE_TIME = 100.seconds
      BEAT_INTERVAL        = 30.seconds

      protected getter id : String
      getter socket : HTTP::WebSocket
      protected getter context : HTTP::Server::Context
      protected getter raw_params : Amber::Router::Params
      protected getter params : Amber::Validators::Params
      protected getter session : Amber::Router::Session::AbstractStore?
      protected getter cookies : Amber::Router::Cookies::Store?
      private property pongs = Array(Time).new
      private property pings = Array(Time).new

      # Add a channel for this socket to listen, publish to
      def self.channel(channel_path, ch)
        @@channels.push({path: channel_path, channel: ch.new(WebSockets.topic_path(channel_path))})
      end

      def self.channels
        @@channels
      end

      def self.get_topic_channel(topic_path)
        topic_channels = @@channels.select { |ch| WebSockets.topic_path(ch[:path]) == topic_path }
        return topic_channels[0][:channel] if topic_channels.any?
      end

      # Broadcast a message to all subscribers of the topic
      #
      # ```crystal
      # UserSocket.broadcast("message", "chats_room:1", "msg:new", {"message" => "test"})
      # ```
      def self.broadcast(event : String, topic : String, subject : String, payload : Hash)
        if channel = get_topic_channel(WebSockets.topic_path(topic))
          channel.rebroadcast!({
            "event"   => event,
            "topic"   => topic,
            "subject" => subject,
            "payload" => payload,
          })
        end
      end
{'fraction_non_alphanumeric': 0.10543522680833674, 'fraction_numerical': 0.005108295872496935, 'mean_word_length': 2.821233411397346, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 13, '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': '4316799', 'n_tokens_mistral': 1417, 'n_tokens_neox': 1352, 'n_words': 362}