mirror of
https://github.com/ergochat/ergo.git
synced 2026-04-26 10:38:23 +02:00
add /v1/whois to API (#2387)
This commit is contained in:
parent
7c8d81ac79
commit
49192e6fa5
24
docs/API.md
24
docs/API.md
@ -179,3 +179,27 @@ The response is a JSON object with fields:
|
||||
* `max`: maximum number of users seen connected at once
|
||||
* `channels`: integer, number of channels currently active
|
||||
* `servers`: integer, number of servers connected in the network
|
||||
|
||||
`/v1/whois`
|
||||
-----------
|
||||
|
||||
This endpoint returns data about the current status of a nickname on the server. The request is a JSON object with fields:
|
||||
|
||||
* `nickname`: string, nickname to query
|
||||
|
||||
The response is a JSON object with fields:
|
||||
|
||||
* `success`: whether the request succeeded (a successful execution returns `true` here even if the nickname is not present)
|
||||
* `present`: whether the nickname is present on the server; if false, the remaining fields are undefined
|
||||
* `nickname`: actual nickname (without case normalization) of the nickname as present on the server
|
||||
* `username`: IRC protocol username field of the user (not to be confused with account name)
|
||||
* `hostname`: hostname of the user
|
||||
* `realname`: realname/gecos of the user
|
||||
* `account`: account name of the user (without case normalization)
|
||||
* `modes`: string of all set user modes
|
||||
* `away`: user's away message if set (omitted if they are not away)
|
||||
* `channels`: list of channels the user is present in. Each channel is an object with fields:
|
||||
* `name`: name of the channel
|
||||
* `mode`: string, highest mode the user has in the channel (omitted if they have no mode)
|
||||
* `join_time`: string, time the user joined the channel (in ISO8601 format)
|
||||
* `session_count`: integer, number of active sessions
|
||||
|
||||
77
irc/api.go
77
irc/api.go
@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/ergochat/ergo/irc/modes"
|
||||
@ -27,6 +28,7 @@ func newAPIHandler(server *Server) http.Handler {
|
||||
|
||||
// use Ergo as a source of truth for authentication in other services:
|
||||
api.mux.HandleFunc("POST /v1/check_auth", api.handleCheckAuth)
|
||||
api.mux.HandleFunc("POST /v1/whois", api.handleWhois)
|
||||
|
||||
// legacy names for /v1/ns endpoints:
|
||||
api.mux.HandleFunc("POST /v1/saregister", api.handleSaregister)
|
||||
@ -422,6 +424,81 @@ type apiListResponse struct {
|
||||
Channels []apiChannelData `json:"channels"`
|
||||
}
|
||||
|
||||
type apiWhoisRequest struct {
|
||||
Nickname string `json:"nickname"`
|
||||
}
|
||||
|
||||
type apiWhoisChannelData struct {
|
||||
Name string `json:"name"`
|
||||
Mode string `json:"mode,omitempty"`
|
||||
JoinTime string `json:"join_time"`
|
||||
}
|
||||
|
||||
type apiWhoisResponse struct {
|
||||
apiGenericResponse
|
||||
Present bool `json:"present"`
|
||||
Nickname string `json:"nickname,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Hostname string `json:"hostname,omitempty"`
|
||||
Realname string `json:"realname,omitempty"`
|
||||
Account string `json:"account"`
|
||||
Modes string `json:"modes,omitempty"`
|
||||
Away string `json:"away,omitempty"`
|
||||
Channels []apiWhoisChannelData `json:"channels"`
|
||||
SessionCount int `json:"session_count"`
|
||||
}
|
||||
|
||||
func (a *ergoAPI) handleWhois(w http.ResponseWriter, r *http.Request) {
|
||||
var request apiWhoisRequest
|
||||
if err := a.decodeJSONRequest(&request, w, r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
response := apiWhoisResponse{
|
||||
apiGenericResponse: apiGenericResponse{Success: true},
|
||||
}
|
||||
|
||||
client := a.server.clients.Get(request.Nickname)
|
||||
if client != nil {
|
||||
response.Present = true
|
||||
details := client.Details()
|
||||
response.Nickname = details.nick
|
||||
response.Username = details.username
|
||||
response.Hostname = details.hostname
|
||||
response.Realname = details.realname
|
||||
if details.account != "" {
|
||||
response.Account = details.accountName
|
||||
}
|
||||
response.Modes = client.ModeString()
|
||||
if away, awayMsg := client.Away(); away {
|
||||
response.Away = awayMsg
|
||||
}
|
||||
response.SessionCount = len(client.Sessions())
|
||||
|
||||
channels := client.Channels()
|
||||
response.Channels = make([]apiWhoisChannelData, 0, len(channels))
|
||||
for _, channel := range channels {
|
||||
present, joinTime, cModes := channel.ClientStatus(client)
|
||||
if !present {
|
||||
continue
|
||||
}
|
||||
chData := apiWhoisChannelData{
|
||||
Name: channel.Name(),
|
||||
JoinTime: joinTime.Format(utils.IRCv3TimestampFormat),
|
||||
}
|
||||
for _, m := range modes.ChannelUserModes {
|
||||
if slices.Contains(cModes, m) {
|
||||
chData.Mode = string(rune(m))
|
||||
break
|
||||
}
|
||||
}
|
||||
response.Channels = append(response.Channels, chData)
|
||||
}
|
||||
}
|
||||
|
||||
a.writeJSONResponse(response, w, r)
|
||||
}
|
||||
|
||||
func (a *ergoAPI) handleList(w http.ResponseWriter, r *http.Request) {
|
||||
channels := a.server.channels.ListableChannels()
|
||||
response := apiListResponse{
|
||||
|
||||
@ -547,12 +547,12 @@ func (channel *Channel) ClientPrefixes(client *Client, isMultiPrefix bool) strin
|
||||
}
|
||||
}
|
||||
|
||||
func (channel *Channel) ClientStatus(client *Client) (present bool, joinTimeSecs int64, cModes modes.Modes) {
|
||||
func (channel *Channel) ClientStatus(client *Client) (present bool, joinTime time.Time, cModes modes.Modes) {
|
||||
channel.stateMutex.RLock()
|
||||
defer channel.stateMutex.RUnlock()
|
||||
memberData, present := channel.members[client]
|
||||
if present {
|
||||
return present, time.Unix(0, memberData.joinTime).Unix(), memberData.modes.AllModes()
|
||||
return present, time.Unix(0, memberData.joinTime), memberData.modes.AllModes()
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user