1
0
mirror of https://github.com/mrusme/neonmodem.git synced 2024-06-16 06:25:23 +00:00

Extended discourse implementation

This commit is contained in:
マリウス 2022-12-29 22:03:38 -05:00
parent 49205c4b7f
commit a34fa46c3d
No known key found for this signature in database
GPG Key ID: 272ED814BF63261F
5 changed files with 356 additions and 1 deletions

200
system/discourse/client.go Normal file
View File

@ -0,0 +1,200 @@
package discourse
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"github.com/hashicorp/go-retryablehttp"
)
type Response struct {
Type string `json:"type"`
Timestamp int64 `json:"timestamp"`
Message string `json:"message"`
Validation map[string]interface{} `json:"validation,omitempty"`
Post PostModel `json:"post,omitempty"`
Posts []PostModel `json:"latest_posts,omitempty"`
}
type RequestError struct {
Response Response
Err error
}
func (re *RequestError) Error() string {
return re.Err.Error()
}
func (re *RequestError) Type() string {
return re.Response.Type
}
func (re *RequestError) Message() string {
return re.Response.Message
}
type Logger interface {
Debug(args ...interface{})
Debugf(format string, args ...interface{})
Info(args ...interface{})
Infof(format string, args ...interface{})
Warn(args ...interface{})
Warnf(format string, args ...interface{})
Error(args ...interface{})
Errorf(format string, args ...interface{})
Fatal(args ...interface{})
Fatalf(format string, args ...interface{})
}
type StdLogger struct {
L Logger
}
func NewStdLogger(l Logger) retryablehttp.Logger {
return &StdLogger{
L: l,
}
}
func (l *StdLogger) Printf(message string, v ...interface{}) {
l.L.Debug(message, v)
}
type ClientConfig struct {
Endpoint string
Credentials map[string]string
HTTPClient *http.Client
Logger Logger
}
type Client struct {
httpClient *retryablehttp.Client
endpoint *url.URL
credentials map[string]string
logger Logger
Posts PostsService
}
func NewDefaultClientConfig(
endpoint string,
credentials map[string]string,
logger Logger,
) ClientConfig {
return ClientConfig{
Endpoint: endpoint,
Credentials: credentials,
HTTPClient: http.DefaultClient,
Logger: logger,
}
}
func NewClient(cc *ClientConfig) *Client {
c := new(Client)
c.logger = cc.Logger
c.httpClient = retryablehttp.NewClient()
c.httpClient.RetryMax = 3
if c.logger != nil {
c.httpClient.Logger = NewStdLogger(c.logger)
}
c.httpClient.HTTPClient = cc.HTTPClient
c.endpoint, _ = url.Parse(cc.Endpoint)
c.credentials = cc.Credentials
fmt.Printf("%v\n", c.credentials)
c.Posts = &PostServiceHandler{client: c}
return c
}
func (c *Client) NewRequest(
ctx context.Context,
method string,
location string,
body interface{},
) (*http.Request, error) {
var parsedURL *url.URL
var req *http.Request
var err error
if parsedURL, err = c.endpoint.Parse(location); err != nil {
return nil, err
}
buffer := new(bytes.Buffer)
if body != nil {
if err = json.NewEncoder(buffer).Encode(body); err != nil {
return nil, err
}
}
if req, err = http.NewRequest(
method,
parsedURL.String(),
buffer,
); err != nil {
return nil, err
}
req.Header.Add("User-Agent", "gobbs")
req.Header.Add("Accept", "application/json")
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Api-Username", c.credentials["username"])
req.Header.Add("Api-Key", c.credentials["key"])
fmt.Printf("%v\n", req)
return req, nil
}
func (c *Client) Do(
ctx context.Context,
req *http.Request,
content *Response,
) error {
var rreq *retryablehttp.Request
var res *http.Response
var body []byte
var err error
if rreq, err = retryablehttp.FromRequest(req); err != nil {
return err
}
rreq = rreq.WithContext(ctx)
if res, err = c.httpClient.Do(rreq); err != nil {
return err
}
defer res.Body.Close()
if body, err = ioutil.ReadAll(res.Body); err != nil {
return err
}
if content != nil {
if err = json.Unmarshal(body, content); err != nil {
return err
}
}
fmt.Printf("%v\n", res)
if res.StatusCode < http.StatusOK ||
res.StatusCode > http.StatusNoContent {
return &RequestError{
Err: errors.New("Non-2xx status code"),
Response: *content,
}
}
return nil
}

View File

@ -2,6 +2,7 @@ package discourse
import (
"bufio"
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
@ -9,6 +10,7 @@ import (
"encoding/json"
"encoding/pem"
"fmt"
"net/http"
"net/url"
"os"
"strings"
@ -17,10 +19,12 @@ import (
"github.com/mrusme/gobbs/models/post"
"github.com/mrusme/gobbs/system/adapter"
"github.com/pkg/browser"
"go.uber.org/zap"
)
type System struct {
config map[string]interface{}
logger *zap.SugaredLogger
}
type UserAPIKey struct {
@ -38,6 +42,10 @@ func (sys *System) SetConfig(cfg *map[string]interface{}) {
sys.config = *cfg
}
func (sys *System) SetLogger(logger *zap.SugaredLogger) {
sys.logger = logger
}
func (sys *System) Load() error {
return nil
@ -149,12 +157,31 @@ func (sys *System) Connect(sysURL string) error {
if sys.config == nil {
sys.config = make(map[string]interface{})
}
sys.config["url"] = sysURL
sys.config["credentials"] = credentials
return nil
}
func (sys *System) ListPosts() ([]post.Post, error) {
credentials := make(map[string]string)
for k, v := range (sys.config["credentials"]).(map[string]interface{}) {
credentials[k] = v.(string)
}
c := NewClient(&ClientConfig{
Endpoint: sys.config["url"].(string),
Credentials: credentials,
HTTPClient: http.DefaultClient,
Logger: sys.logger,
})
posts, err := c.Posts.List(context.Background())
if err != nil {
return []post.Post{}, err
}
fmt.Printf("%v\n", posts)
return []post.Post{}, nil
}

115
system/discourse/posts.go Normal file
View File

@ -0,0 +1,115 @@
package discourse
import (
"context"
"net/http"
)
const PostsBaseURL = "/posts"
type PostModel struct {
ID string `json:"id"`
Name string `json:"name"`
Username string `json:"username"`
AvatarTemplate string `json:"avater_template"`
CreatedAt string `json:"created_at"`
Cooked string `json:"cooked"`
PostNumber int `json:"post_number"`
PostType int `json:"post_type"`
UpdatedAt string `json:"updated_at"`
ReplyCount int `json:"reply_count"`
IncomingLinkCount int `json:"incoming_link_count"`
Reads int `json:"reads"`
ReadersCount int `json:"readers_count"`
Score int `json:"score"`
Yours bool `json:"yours"`
TopicID int `json:"topic_id"`
TopicSlug string `json:"topic_slug"`
TopicTitle string `json:"topic_title"`
TopicHTMLTitle string `json:"topic_html_title"`
CategoryID int `json:"category_id"`
DisplayUsername string `json:"display_username"`
PrimaryGroupName string `json:"primary_group_name"`
FlairName string `json:"flair_name"`
FlairURL string `json:"flair_url"`
FlairBGColor string `json:"flair_bg_color"`
FlairColor string `json:"flair_color"`
Version int `json:"version"`
CanEdit bool `json:"can_edit"`
CanDelete bool `json:"can_delete"`
CanRecover bool `json:"can_recover"`
CanWiki bool `json:"can_wiki"`
UserTitle string `json:"user_title"`
Raw string `json:"raw"`
ActionsSummary struct {
ID int `json:"id"`
CanAct bool `json:"can_act"`
} `json:"actions_summary"`
Moderator bool `json:"moderator"`
Admin bool `json:"admin"`
Staff bool `json:"staff"`
UserID int `json:"user_id"`
Hidden bool `json:"hidden"`
TrustLevel int `json:"trust_level"`
DeletedAt string `json:"deleted_at"`
UserDeleted bool `json:"user_deleted"`
EditReason string `json:"edit_reason"`
CanViewEditHistory bool `json:"can_view_edit_history"`
Wiki bool `json:"wiki"`
ReviewableID string `json:"reviewable_id"`
ReviewableScoreCount int `json:"reviewable_score_count"`
ReviewableScorePendingCount int `json:"reviewable_score_pending_count"`
}
type PostsService interface {
Show(
ctx context.Context,
id string,
) (PostModel, error)
List(
ctx context.Context,
) ([]PostModel, error)
}
type PostServiceHandler struct {
client *Client
}
// Show
func (a *PostServiceHandler) Show(
ctx context.Context,
id string,
) (PostModel, error) {
uri := PostsBaseURL + "/" + id + ".json"
req, err := a.client.NewRequest(ctx, http.MethodGet, uri, nil)
if err != nil {
return PostModel{}, err
}
response := new(Response)
if err = a.client.Do(ctx, req, response); err != nil {
return PostModel{}, err
}
return response.Post, nil
}
// List
func (a *PostServiceHandler) List(
ctx context.Context,
) ([]PostModel, error) {
uri := PostsBaseURL + ".json"
req, err := a.client.NewRequest(ctx, http.MethodGet, uri, nil)
if err != nil {
return []PostModel{}, err
}
response := new(Response)
if err = a.client.Do(ctx, req, response); err != nil {
return []PostModel{}, err
}
return response.Posts, nil
}

View File

@ -3,10 +3,12 @@ package lemmy
import (
"github.com/mrusme/gobbs/models/post"
"github.com/mrusme/gobbs/system/adapter"
"go.uber.org/zap"
)
type System struct {
config map[string]interface{}
logger *zap.SugaredLogger
}
func (sys *System) GetConfig() map[string]interface{} {
@ -17,6 +19,10 @@ func (sys *System) SetConfig(cfg *map[string]interface{}) {
sys.config = *cfg
}
func (sys *System) SetLogger(logger *zap.SugaredLogger) {
sys.logger = logger
}
func (sys *System) Connect(sysURL string) error {
return nil
}

View File

@ -7,11 +7,13 @@ import (
"github.com/mrusme/gobbs/system/adapter"
"github.com/mrusme/gobbs/system/discourse"
"github.com/mrusme/gobbs/system/lemmy"
"go.uber.org/zap"
)
type System interface {
GetConfig() map[string]interface{}
SetConfig(cfg *map[string]interface{})
SetLogger(logger *zap.SugaredLogger)
GetCapabilities() []adapter.Capability
Connect(sysURL string) error
@ -20,7 +22,11 @@ type System interface {
ListPosts() ([]post.Post, error)
}
func New(sysType string, sysConfig *map[string]interface{}) (System, error) {
func New(
sysType string,
sysConfig *map[string]interface{},
logger *zap.SugaredLogger,
) (System, error) {
var sys System
switch sysType {
@ -33,6 +39,7 @@ func New(sysType string, sysConfig *map[string]interface{}) (System, error) {
}
sys.SetConfig(sysConfig)
sys.SetLogger(logger)
err := sys.Load()
if err != nil {
return nil, err