Introduce model/ and repository/ structure under lib/
Models: Message, WikiPage, Game (typed structs instead of raw hashes) Repositories: MessageBoard, OnlineUsers, WikiClient, CatalogClient bbs.rb uses attribute access (page.title, game.play_path, …) throughout Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
29
lib/repository/catalog.rb
Normal file
29
lib/repository/catalog.rb
Normal file
@@ -0,0 +1,29 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'net/http'
|
||||
require 'json'
|
||||
require 'uri'
|
||||
require_relative '../model/game'
|
||||
|
||||
class CatalogClient
|
||||
API_URL = 'https://games.teletype.hu/api/software'
|
||||
GAMES_URL = 'https://games.teletype.hu'
|
||||
|
||||
def fetch
|
||||
uri = URI(API_URL)
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = uri.scheme == 'https'
|
||||
http.open_timeout = 12
|
||||
http.read_timeout = 12
|
||||
data = JSON.parse(http.get(uri.path).body)
|
||||
entries = data.is_a?(Hash) ? (data['softwares'] || []) : data
|
||||
entries.filter_map { |e| Game.new(e) if e.is_a?(Hash) }
|
||||
rescue => e
|
||||
warn "Catalog fetch error: #{e}"
|
||||
[]
|
||||
end
|
||||
|
||||
def play_url(path)
|
||||
"#{GAMES_URL}#{path}"
|
||||
end
|
||||
end
|
||||
42
lib/repository/message_board.rb
Normal file
42
lib/repository/message_board.rb
Normal file
@@ -0,0 +1,42 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'csv'
|
||||
require 'time'
|
||||
require 'fileutils'
|
||||
require_relative '../model/message'
|
||||
|
||||
class MessageBoard
|
||||
def initialize(path)
|
||||
@path = path
|
||||
@messages = []
|
||||
@mu = Mutex.new
|
||||
load_csv
|
||||
end
|
||||
|
||||
def append(username, text)
|
||||
msg = Message.new(Time.now.strftime('%m-%d %H:%M'), username, text)
|
||||
@mu.synchronize do
|
||||
@messages << msg
|
||||
FileUtils.mkdir_p(File.dirname(@path))
|
||||
CSV.open(@path, 'a') { |csv| csv << [msg.timestamp, msg.username, msg.text] }
|
||||
end
|
||||
msg
|
||||
end
|
||||
|
||||
def last(n = 30)
|
||||
@mu.synchronize { @messages.last(n) }
|
||||
end
|
||||
|
||||
def count
|
||||
@mu.synchronize { @messages.size }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_csv
|
||||
return unless File.exist?(@path)
|
||||
CSV.foreach(@path) { |row| @messages << Message.new(*row) }
|
||||
rescue => e
|
||||
warn "MessageBoard load error: #{e}"
|
||||
end
|
||||
end
|
||||
24
lib/repository/online_users.rb
Normal file
24
lib/repository/online_users.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class OnlineUsers
|
||||
def initialize
|
||||
@users = {}
|
||||
@mu = Mutex.new
|
||||
end
|
||||
|
||||
def add(session_id, name)
|
||||
@mu.synchronize { @users[session_id] = name }
|
||||
end
|
||||
|
||||
def remove(session_id)
|
||||
@mu.synchronize { @users.delete(session_id) }
|
||||
end
|
||||
|
||||
def snapshot
|
||||
@mu.synchronize { @users.dup }
|
||||
end
|
||||
|
||||
def count
|
||||
@mu.synchronize { @users.size }
|
||||
end
|
||||
end
|
||||
63
lib/repository/wiki.rb
Normal file
63
lib/repository/wiki.rb
Normal file
@@ -0,0 +1,63 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'net/http'
|
||||
require 'json'
|
||||
require 'uri'
|
||||
require_relative '../model/wiki_page'
|
||||
|
||||
class WikiClient
|
||||
BASE_URL = 'https://wiki.teletype.hu'
|
||||
|
||||
def initialize(token: nil)
|
||||
@token = token
|
||||
end
|
||||
|
||||
def list(tag)
|
||||
query = <<~GQL
|
||||
{ pages { list(orderBy: CREATED, orderByDirection: DESC, tags: ["#{tag}"]) {
|
||||
id path title description createdAt locale
|
||||
}}}
|
||||
GQL
|
||||
(graphql(query).dig('data', 'pages', 'list') || []).map do |p|
|
||||
WikiPage.new(
|
||||
id: p['id'],
|
||||
path: p['path'],
|
||||
title: p['title'],
|
||||
description: p['description'],
|
||||
created_at: p['createdAt'],
|
||||
locale: p['locale']
|
||||
)
|
||||
end
|
||||
rescue => e
|
||||
warn "Wiki list error: #{e}"
|
||||
[]
|
||||
end
|
||||
|
||||
def content(page_id)
|
||||
query = "{ pages { single(id: #{page_id}) { content } } }"
|
||||
graphql(query).dig('data', 'pages', 'single', 'content') || ''
|
||||
rescue => e
|
||||
warn "Wiki content error: #{e}"
|
||||
''
|
||||
end
|
||||
|
||||
def page_url(locale, path)
|
||||
"#{BASE_URL}/#{locale}/#{path}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def graphql(query)
|
||||
uri = URI("#{BASE_URL}/graphql")
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = uri.scheme == 'https'
|
||||
http.open_timeout = 12
|
||||
http.read_timeout = 12
|
||||
|
||||
req = Net::HTTP::Post.new(uri.path, 'Content-Type' => 'application/json')
|
||||
req['Authorization'] = "Bearer #{@token}" if @token
|
||||
req.body = JSON.generate(query: query)
|
||||
|
||||
JSON.parse(http.request(req).body)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user