<commit_before># coding: utf-8

# require 'bundler/setup'
require './star_chat/models'
require 'sinatra'
require 'erubis'

set :server, :thin
set :sessions, false
set :erb, escape_html: true
set :streams, []
set :launched_time, Time.now.to_i

require './config'

helpers do

  def launched_time
    settings.launched_time
  end

  def protect!
    halt 401 unless authorized?
  end

  def authorized?
    if !auth.provided? or !auth.basic? or !auth.credentials
      return false
    end
    user_name, password = auth.credentials
    def password.inspect
      '(secret)'
    end
    return false unless user_name
    return false if user_name.empty?
    return false unless password
    StarChat::User.auth?(user_name, password)
  end

  def auth
    @auth ||= Rack::Auth::Basic::Request.new(request.env)
  end

  def current_user
    return @current_user if @current_user
    return nil unless authorized?
    user_name = auth.credentials[0]
    user = StarChat::User.find(user_name)
    return @current_user = user if user
    user = StarChat::User.new(user_name).save
    lobby_channel = (StarChat::Channel.find('Lobby') or
                     StarChat::Channel.new('Lobby').save)
    StarChat::Subscribing.save(lobby_channel, user)
    broadcast(type: 'subscribing',
              channel_name: lobby_channel.name,
              user_name: user_name) do |user_name|
      return false unless u = StarChat::User.find(user_name)
      lobby_channel.users.any?{|u2| u2.name == u.name}
    end
    @current_user = user
  end

  def broadcast(values)
    json = values.to_json
    settings.streams.each do |user_name, connection|
      next unless yield(user_name)
      # TODO: error handling?
      connection << json << "\n"
    end
  end

  def uri_encode(str)
    # Rack::Utils.escape is not for URI but for application/x-www-form-urlencoded
    Rack::Utils.escape(str).gsub('+', '%20')
  end

  def uri_decode(str)
    Rack::Utils.unescape(str.gsub('+', '%2B'))
  end

end

get '/', provides: :html do
  erb(:index)
end

before %r{^/users/([^/]+)} do
  protect!
  user_name = params[:captures][0]
  @user = StarChat::User.find(user_name)
  if @user.nil?
    if current_user.name == user_name
      @user = StarChat::User.new(user_name).save
    else
      halt 404
    end
  end
  if (request.put? or request.delete? or request.post?) and
      current_user.name != @user.name
    halt 401
  end
end

get '/users/:user_name', provides: :json do
  if @user.name == current_user.name
    current_user.to_json
  else
    {
      name: @user.name,
      nick: @user.nick,
    }.to_json
  end
end

put '/users/:user_name', provides: :json do
  if params[:nick]
    # TODO: check uniqueness?
    nick = params[:nick].to_s.strip.gsub(/[[:cntrl:]]/, '')[0, 32]
    if current_user.nick != nick
      current_user.nick = nick
      broadcast(type: 'user',
                user: {
                  name: current_user.name,
                  nick: current_user.nick,
                }) do
        true
      end
    end
  end
  if params[:keywords] and params[:keywords].kind_of?(Array)
    current_user.keywords = params[:keywords]
  end
  current_user.save
  200
end

get '/users/:user_name/ping', provides: :json do
  halt 401 if @user.name != current_user.name
  {
    result: 'pong',
  }.to_json
end

get '/users/:user_name/channels', provides: :json do
  if @user.name == current_user.name
    current_user.channels.to_json
  else
    # TODO: 直す
    [].to_json
  end
end

get '/users/:user_name/stream', provides: :json do
  halt 401 if @user.name != current_user.name
  stream(:keep_open) do |out|
    subscribe = [@user.name, out]
    settings.streams << subscribe
    if params[:start_time]
      start_time = params[:start_time].to_i
      end_time   = Time.now.to_i + 1
      packets = current_user.channels.inject([]) do |msgs, channel|
        msgs.concat(channel.messages_by_time_span(start_time, end_time).map do |msg|
                      {
                        type: 'message',
                        message: msg,
                      }
                    end.to_a)
      end
      packets.sort do |a, b|
        a[:message].id <=> b[:message].id
      end.each do |packet|
        out << packet.to_json << "\n"
      end
    end
    out.callback do
      settings.streams.delete(subscribe)
    end
    out.errback do
      settings.streams.delete(subscribe)
    end
  end
end

before %r{^/channels/([^/]+)} do
  protect!
  channel_name = params[:captures][0]
  # TODO: only for members?
  @channel = StarChat::Channel.find(channel_name)
  if @channel
    if (request.put? or request.delete? or request.post?) and
        !current_user.subscribing?(@channel)
      halt 401
    end<commit_msg>Returns 406 when JSON is not accepted.<commit_after># coding: utf-8

# require 'bundler/setup'
require './star_chat/models'
require 'sinatra'
require 'erubis'

set :server, :thin
set :sessions, false
set :erb, escape_html: true
set :streams, []
set :launched_time, Time.now.to_i

require './config'

helpers do

  def launched_time
    settings.launched_time
  end

  def protect!
    halt 401 unless authorized?
  end

  def authorized?
    if !auth.provided? or !auth.basic? or !auth.credentials
      return false
    end
    user_name, password = auth.credentials
    def password.inspect
      '(secret)'
    end
    return false unless user_name
    return false if user_name.empty?
    return false unless password
    StarChat::User.auth?(user_name, password)
  end

  def auth
    @auth ||= Rack::Auth::Basic::Request.new(request.env)
  end

  def current_user
    return @current_user if @current_user
    return nil unless authorized?
    user_name = auth.credentials[0]
    user = StarChat::User.find(user_name)
    return @current_user = user if user
    user = StarChat::User.new(user_name).save
    lobby_channel = (StarChat::Channel.find('Lobby') or
                     StarChat::Channel.new('Lobby').save)
    StarChat::Subscribing.save(lobby_channel, user)
    broadcast(type: 'subscribing',
              channel_name: lobby_channel.name,
              user_name: user_name) do |user_name|
      return false unless u = StarChat::User.find(user_name)
      lobby_channel.users.any?{|u2| u2.name == u.name}
    end
    @current_user = user
  end

  def broadcast(values)
    json = values.to_json
    settings.streams.each do |user_name, connection|
      next unless yield(user_name)
      # TODO: error handling?
      connection << json << "\n"
    end
  end

  def uri_encode(str)
    # Rack::Utils.escape is not for URI but for application/x-www-form-urlencoded
    Rack::Utils.escape(str).gsub('+', '%20')
  end

  def uri_decode(str)
    Rack::Utils.unescape(str.gsub('+', '%2B'))
  end

end

get '/', provides: :html do
  erb(:index)
end

before %r{^/users/([^/]+)} do
  protect!
  user_name = params[:captures][0]
  @user = StarChat::User.find(user_name)
  if @user.nil?
    if current_user.name == user_name
      @user = StarChat::User.new(user_name).save
    else
      halt 404
    end
  end
  if (request.put? or request.delete? or request.post?) and
      current_user.name != @user.name
    halt 401
  end
end

get '/users/:user_name', provides: :json do
  if @user.name == current_user.name
    current_user.to_json
  else
    {
      name: @user.name,
      nick: @user.nick,
    }.to_json
  end
end

put '/users/:user_name', provides: :json do
  if params[:nick]
    # TODO: check uniqueness?
    nick = params[:nick].to_s.strip.gsub(/[[:cntrl:]]/, '')[0, 32]
    if current_user.nick != nick
      current_user.nick = nick
      broadcast(type: 'user',
                user: {
                  name: current_user.name,
                  nick: current_user.nick,
                }) do
        true
      end
    end
  end
  if params[:keywords] and params[:keywords].kind_of?(Array)
    current_user.keywords = params[:keywords]
  end
  current_user.save
  200
end

get '/users/:user_name/ping', provides: :json do
  halt 401 if @user.name != current_user.name
  {
    result: 'pong',
  }.to_json
end

get '/users/:user_name/channels', provides: :json do
  if @user.name == current_user.name
    current_user.channels.to_json
  else
    # TODO: 直す
    [].to_json
  end
end

get '/users/:user_name/stream', provides: :json do
  halt 401 if @user.name != current_user.name
  stream(:keep_open) do |out|
    subscribe = [@user.name, out]
    settings.streams << subscribe
    if params[:start_time]
      start_time = params[:start_time].to_i
      end_time   = Time.now.to_i + 1
      packets = current_user.channels.inject([]) do |msgs, channel|
        msgs.concat(channel.messages_by_time_span(start_time, end_time).map do |msg|
                      {
                        type: 'message',
                        message: msg,
                      }
                    end.to_a)
      end
      packets.sort do |a, b|
        a[:message].id <=> b[:message].id
      end.each do |packet|
        out << packet.to_json << "\n"
      end
    end
    out.callback do
      settings.streams.delete(subscribe)
    end
    out.errback do
      settings.streams.delete(subscribe)
    end
  end
end

before %r{^/.+} do
  halt 406 unless request.accept?(mime_type(:json))
end

before %r{^/channels/([^/]+)} do
  protect!
  channel_name = params[:captures][0]
  # TODO: only for members?
  @channel = StarChat::Channel.find(channel_name)
  if @channel
    if (request.put? or request.delete? or request.post?) and
        !current_user.subscribing?(@channel)
      halt 401
    end
{'fraction_non_alphanumeric': 0.12220707017727893, 'fraction_numerical': 0.00776250917864261, 'mean_word_length': 2.5896084337349397, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, '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': '687646', 'n_tokens_mistral': 3260, 'n_tokens_neox': 3158, 'n_words': 771}