init
This commit is contained in:
commit
ccdd7c3280
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
config.yaml
|
||||||
|
statedata
|
||||||
|
config.yml
|
44
client.go
Normal file
44
client.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"maunium.net/go/mautrix"
|
||||||
|
"maunium.net/go/mautrix/id"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newMatrixClient(config *botConfig) *mautrix.Client {
|
||||||
|
fmt.Println("Logging into", config.Homeserver, "as", config.Username)
|
||||||
|
var client *mautrix.Client
|
||||||
|
var err error
|
||||||
|
if config.Token == "" {
|
||||||
|
client, err = mautrix.NewClient(config.Homeserver, "", "")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Println("using token login")
|
||||||
|
client, err = mautrix.NewClient(config.Homeserver, id.NewUserID(config.Username, config.Domain), config.Token)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client.Store = NewLazyMemStore(config.Statefile)
|
||||||
|
if config.Token == "" {
|
||||||
|
loginRes, err := client.Login(&mautrix.ReqLogin{
|
||||||
|
Type: "m.login.password",
|
||||||
|
Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: config.Username},
|
||||||
|
Password: config.Password,
|
||||||
|
StoreCredentials: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
config.Token = loginRes.AccessToken
|
||||||
|
log.Println("Login succesful, saving access_token to config file")
|
||||||
|
} else {
|
||||||
|
log.Println("skipping login since token provided")
|
||||||
|
}
|
||||||
|
return client
|
||||||
|
}
|
31
config.go
Normal file
31
config.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type botConfig struct {
|
||||||
|
Homeserver string `yaml:"homeserver"`
|
||||||
|
Domain string `yaml:"domain"`
|
||||||
|
Dimension string `yaml:"dimension"`
|
||||||
|
Username string `yaml:"username"`
|
||||||
|
Password string `yaml:"password"`
|
||||||
|
Statefile string `yaml:"statefile"`
|
||||||
|
Token string `yaml:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadConfig(filename string) *botConfig {
|
||||||
|
yamlFile, err := ioutil.ReadFile(filename)
|
||||||
|
cnf := &botConfig{}
|
||||||
|
if err == nil {
|
||||||
|
err = yaml.Unmarshal(yamlFile, cnf)
|
||||||
|
} else {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return cnf
|
||||||
|
}
|
14
go.mod
Normal file
14
go.mod
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
module git.saintnet.tech/stryan/nunbot
|
||||||
|
|
||||||
|
go 1.18
|
||||||
|
|
||||||
|
require (
|
||||||
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
|
maunium.net/go/mautrix v0.11.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/stretchr/testify v1.8.0 // indirect
|
||||||
|
golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9 // indirect
|
||||||
|
golang.org/x/net v0.0.0-20220513224357-95641704303c // indirect
|
||||||
|
)
|
23
go.sum
Normal file
23
go.sum
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9 h1:NUzdAbFtCJSXU20AOXgeqaUwg8Ypg4MPYmL+d+rsB5c=
|
||||||
|
golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/net v0.0.0-20220513224357-95641704303c h1:nF9mHSvoKBLkQNQhJZNsc66z2UzAMUbLGjC95CF3pU0=
|
||||||
|
golang.org/x/net v0.0.0-20220513224357-95641704303c/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
maunium.net/go/mautrix v0.11.0 h1:B1FBHcvE4Mud+AC+zgNQQOw0JxSVrt40watCejhVA7w=
|
||||||
|
maunium.net/go/mautrix v0.11.0/go.mod h1:K29EcHwsNg6r7fMfwvi0GHQ9o5wSjqB9+Q8RjCIQEjA=
|
85
lazystore.go
Normal file
85
lazystore.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/gob"
|
||||||
|
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"maunium.net/go/mautrix"
|
||||||
|
"maunium.net/go/mautrix/id"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LazyMemStore struct {
|
||||||
|
mem *mautrix.InMemoryStore
|
||||||
|
NextBatch map[id.UserID]string
|
||||||
|
saveFile string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLazyMemStore(fileloc string) *LazyMemStore {
|
||||||
|
return &LazyMemStore{
|
||||||
|
mem: mautrix.NewInMemoryStore(),
|
||||||
|
NextBatch: make(map[id.UserID]string),
|
||||||
|
saveFile: fileloc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LazyMemStore) SaveFilterID(userID id.UserID, filterID string) {
|
||||||
|
l.mem.SaveFilterID(userID, filterID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LazyMemStore) LoadFilterID(userID id.UserID) string {
|
||||||
|
return l.mem.LoadFilterID(userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LazyMemStore) SaveNextBatch(userID id.UserID, nextBatchToken string) {
|
||||||
|
b := new(bytes.Buffer)
|
||||||
|
l.NextBatch[userID] = nextBatchToken
|
||||||
|
e := gob.NewEncoder(b)
|
||||||
|
err := e.Encode(l.NextBatch)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(l.saveFile, b.Bytes(), 0666); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LazyMemStore) LoadNextBatch(userID id.UserID) string {
|
||||||
|
|
||||||
|
dat, err := os.ReadFile(l.saveFile)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
b := new(bytes.Buffer)
|
||||||
|
e := gob.NewEncoder(b)
|
||||||
|
err := e.Encode(l.NextBatch)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(l.saveFile, b.Bytes(), 0666); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
dat, err = os.ReadFile(l.saveFile)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d := gob.NewDecoder(bytes.NewBuffer(dat))
|
||||||
|
err = d.Decode(&l.NextBatch)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return l.NextBatch[userID]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LazyMemStore) SaveRoom(room *mautrix.Room) {
|
||||||
|
l.mem.SaveRoom(room)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LazyMemStore) LoadRoom(roomID id.RoomID) *mautrix.Room {
|
||||||
|
return l.mem.LoadRoom(roomID)
|
||||||
|
}
|
90
main.go
Normal file
90
main.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"maunium.net/go/mautrix"
|
||||||
|
"maunium.net/go/mautrix/event"
|
||||||
|
)
|
||||||
|
|
||||||
|
//GitTag is current git tag
|
||||||
|
var GitTag string
|
||||||
|
|
||||||
|
//GitCommit is current git commit
|
||||||
|
var GitCommit string
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
conf := loadConfig("config.yaml")
|
||||||
|
matrixClient := newMatrixClient(conf)
|
||||||
|
//redditClient := newRedditClient(conf)
|
||||||
|
syncer := matrixClient.Syncer.(*mautrix.DefaultSyncer)
|
||||||
|
syncer.OnEventType(event.EventMessage, func(source mautrix.EventSource, evt *event.Event) {
|
||||||
|
if evt.Sender == matrixClient.UserID {
|
||||||
|
return //ignore events from self
|
||||||
|
}
|
||||||
|
fmt.Printf("<%[1]s> %[4]s (%[2]s/%[3]s)\n", evt.Sender, evt.Type.String(), evt.ID, evt.Content.AsMessage().Body)
|
||||||
|
body := evt.Content.AsMessage().Body
|
||||||
|
bodyS := strings.Split(body, " ")
|
||||||
|
if bodyS[0] != "!nun" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(bodyS) < 2 {
|
||||||
|
return //nothing to parse
|
||||||
|
}
|
||||||
|
switch bodyS[1] {
|
||||||
|
case "version":
|
||||||
|
// print version
|
||||||
|
if GitTag != "" {
|
||||||
|
matrixClient.SendText(evt.RoomID, "NunBot version "+GitTag)
|
||||||
|
} else {
|
||||||
|
matrixClient.SendText(evt.RoomID, "NunBot version "+GitCommit)
|
||||||
|
}
|
||||||
|
case "help":
|
||||||
|
matrixClient.SendText(evt.RoomID, "Supported commands: version,stats")
|
||||||
|
default:
|
||||||
|
//command not found
|
||||||
|
matrixClient.SendText(evt.RoomID, "command not recognized")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
syncer.OnEventType(event.StateMember, func(source mautrix.EventSource, evt *event.Event) {
|
||||||
|
fmt.Printf("<%[1]s> %[4]s (%[2]s/%[3]s)\n", evt.Sender, evt.Type.String(), evt.ID, evt.Content.AsMessage().Body)
|
||||||
|
if evt.Content.AsMember().Membership.IsInviteOrJoin() {
|
||||||
|
_, err := matrixClient.JoinRoomByID(evt.RoomID)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("error joining room %v", evt.RoomID)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("joined room %v", evt.RoomID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
var curPost post
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
time.Sleep(30 * time.Second)
|
||||||
|
newPost := getNewestPost("LittleNuns")
|
||||||
|
if curPost.Title != newPost.Title {
|
||||||
|
curPost = newPost
|
||||||
|
roomResp, err := matrixClient.JoinedRooms()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error getting joined rooms: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rooms := roomResp.JoinedRooms
|
||||||
|
for _, room := range rooms {
|
||||||
|
matrixClient.SendText(room, fmt.Sprintf("%v\n%v", curPost.Title, curPost.Link))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
err := matrixClient.Sync()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
54
reddit.go
Normal file
54
reddit.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type post struct {
|
||||||
|
Title string
|
||||||
|
Link string
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNewestPost(subreddit string) post {
|
||||||
|
var resp redditResp
|
||||||
|
//building request from scratch because reddit api is weird
|
||||||
|
url := fmt.Sprintf("https://www.reddit.com/r/%v/new.json?sort=new&limit=1", subreddit)
|
||||||
|
req, _ := http.NewRequest("GET", url, nil)
|
||||||
|
req.Header.Set("User-Agent", "Custom Agent")
|
||||||
|
req.Header.Set("Host", "reddit.com")
|
||||||
|
var defaultClient = http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
TLSNextProto: map[string]func(authority string, c *tls.Conn) http.RoundTripper{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
res, err := defaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Got %v, retrying in 5s", err)
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
res, err = http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Got %v, not trying again", err)
|
||||||
|
return post{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
body, readErr := ioutil.ReadAll(res.Body)
|
||||||
|
if readErr != nil {
|
||||||
|
log.Fatalf("error reading reddit resp: %v", readErr)
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(body, &resp)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("unmarshal error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return post{
|
||||||
|
Title: resp.Data.Children[0].Data.Title,
|
||||||
|
Link: resp.Data.Children[0].Data.URL,
|
||||||
|
}
|
||||||
|
}
|
164
reddit_struct.go
Normal file
164
reddit_struct.go
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type redditResp struct {
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
Data struct {
|
||||||
|
After string `json:"after"`
|
||||||
|
Dist int `json:"dist"`
|
||||||
|
Modhash string `json:"modhash"`
|
||||||
|
GeoFilter string `json:"geo_filter"`
|
||||||
|
Children []struct {
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
Data struct {
|
||||||
|
ApprovedAtUtc interface{} `json:"approved_at_utc"`
|
||||||
|
Subreddit string `json:"subreddit"`
|
||||||
|
Selftext string `json:"selftext"`
|
||||||
|
AuthorFullname string `json:"author_fullname"`
|
||||||
|
Saved bool `json:"saved"`
|
||||||
|
ModReasonTitle interface{} `json:"mod_reason_title"`
|
||||||
|
Gilded int `json:"gilded"`
|
||||||
|
Clicked bool `json:"clicked"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
LinkFlairRichtext []interface{} `json:"link_flair_richtext"`
|
||||||
|
SubredditNamePrefixed string `json:"subreddit_name_prefixed"`
|
||||||
|
Collections []struct {
|
||||||
|
Permalink string `json:"permalink"`
|
||||||
|
LinkIds []string `json:"link_ids"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
CreatedAtUtc float64 `json:"created_at_utc"`
|
||||||
|
SubredditID string `json:"subreddit_id"`
|
||||||
|
AuthorName string `json:"author_name"`
|
||||||
|
CollectionID string `json:"collection_id"`
|
||||||
|
AuthorID string `json:"author_id"`
|
||||||
|
LastUpdateUtc float64 `json:"last_update_utc"`
|
||||||
|
DisplayLayout string `json:"display_layout"`
|
||||||
|
} `json:"collections"`
|
||||||
|
Hidden bool `json:"hidden"`
|
||||||
|
Pwls interface{} `json:"pwls"`
|
||||||
|
LinkFlairCSSClass interface{} `json:"link_flair_css_class"`
|
||||||
|
Downs int `json:"downs"`
|
||||||
|
ThumbnailHeight int `json:"thumbnail_height"`
|
||||||
|
TopAwardedType interface{} `json:"top_awarded_type"`
|
||||||
|
HideScore bool `json:"hide_score"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Quarantine bool `json:"quarantine"`
|
||||||
|
LinkFlairTextColor string `json:"link_flair_text_color"`
|
||||||
|
UpvoteRatio float64 `json:"upvote_ratio"`
|
||||||
|
AuthorFlairBackgroundColor string `json:"author_flair_background_color"`
|
||||||
|
Ups int `json:"ups"`
|
||||||
|
TotalAwardsReceived int `json:"total_awards_received"`
|
||||||
|
MediaEmbed struct {
|
||||||
|
} `json:"media_embed"`
|
||||||
|
ThumbnailWidth int `json:"thumbnail_width"`
|
||||||
|
AuthorFlairTemplateID string `json:"author_flair_template_id"`
|
||||||
|
IsOriginalContent bool `json:"is_original_content"`
|
||||||
|
UserReports []interface{} `json:"user_reports"`
|
||||||
|
SecureMedia interface{} `json:"secure_media"`
|
||||||
|
IsRedditMediaDomain bool `json:"is_reddit_media_domain"`
|
||||||
|
IsMeta bool `json:"is_meta"`
|
||||||
|
Category interface{} `json:"category"`
|
||||||
|
SecureMediaEmbed struct {
|
||||||
|
} `json:"secure_media_embed"`
|
||||||
|
LinkFlairText interface{} `json:"link_flair_text"`
|
||||||
|
CanModPost bool `json:"can_mod_post"`
|
||||||
|
Score int `json:"score"`
|
||||||
|
ApprovedBy interface{} `json:"approved_by"`
|
||||||
|
IsCreatedFromAdsUI bool `json:"is_created_from_ads_ui"`
|
||||||
|
AuthorPremium bool `json:"author_premium"`
|
||||||
|
Thumbnail string `json:"thumbnail"`
|
||||||
|
Edited bool `json:"edited"`
|
||||||
|
AuthorFlairCSSClass string `json:"author_flair_css_class"`
|
||||||
|
AuthorFlairRichtext []struct {
|
||||||
|
A string `json:"a,omitempty"`
|
||||||
|
E string `json:"e"`
|
||||||
|
U string `json:"u,omitempty"`
|
||||||
|
T string `json:"t,omitempty"`
|
||||||
|
} `json:"author_flair_richtext"`
|
||||||
|
Gildings struct {
|
||||||
|
} `json:"gildings"`
|
||||||
|
PostHint string `json:"post_hint"`
|
||||||
|
ContentCategories interface{} `json:"content_categories"`
|
||||||
|
IsSelf bool `json:"is_self"`
|
||||||
|
SubredditType string `json:"subreddit_type"`
|
||||||
|
Created float64 `json:"created"`
|
||||||
|
LinkFlairType string `json:"link_flair_type"`
|
||||||
|
Wls interface{} `json:"wls"`
|
||||||
|
RemovedByCategory interface{} `json:"removed_by_category"`
|
||||||
|
BannedBy interface{} `json:"banned_by"`
|
||||||
|
AuthorFlairType string `json:"author_flair_type"`
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
AllowLiveComments bool `json:"allow_live_comments"`
|
||||||
|
SelftextHTML interface{} `json:"selftext_html"`
|
||||||
|
Likes interface{} `json:"likes"`
|
||||||
|
SuggestedSort interface{} `json:"suggested_sort"`
|
||||||
|
BannedAtUtc interface{} `json:"banned_at_utc"`
|
||||||
|
URLOverriddenByDest string `json:"url_overridden_by_dest"`
|
||||||
|
ViewCount interface{} `json:"view_count"`
|
||||||
|
Archived bool `json:"archived"`
|
||||||
|
NoFollow bool `json:"no_follow"`
|
||||||
|
IsCrosspostable bool `json:"is_crosspostable"`
|
||||||
|
Pinned bool `json:"pinned"`
|
||||||
|
Over18 bool `json:"over_18"`
|
||||||
|
Preview struct {
|
||||||
|
Images []struct {
|
||||||
|
Source struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
Width int `json:"width"`
|
||||||
|
Height int `json:"height"`
|
||||||
|
} `json:"source"`
|
||||||
|
Resolutions []struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
Width int `json:"width"`
|
||||||
|
Height int `json:"height"`
|
||||||
|
} `json:"resolutions"`
|
||||||
|
Variants struct {
|
||||||
|
} `json:"variants"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
} `json:"images"`
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
} `json:"preview"`
|
||||||
|
AllAwardings []interface{} `json:"all_awardings"`
|
||||||
|
Awarders []interface{} `json:"awarders"`
|
||||||
|
MediaOnly bool `json:"media_only"`
|
||||||
|
CanGild bool `json:"can_gild"`
|
||||||
|
Spoiler bool `json:"spoiler"`
|
||||||
|
Locked bool `json:"locked"`
|
||||||
|
AuthorFlairText string `json:"author_flair_text"`
|
||||||
|
TreatmentTags []interface{} `json:"treatment_tags"`
|
||||||
|
Visited bool `json:"visited"`
|
||||||
|
RemovedBy interface{} `json:"removed_by"`
|
||||||
|
ModNote interface{} `json:"mod_note"`
|
||||||
|
Distinguished interface{} `json:"distinguished"`
|
||||||
|
SubredditID string `json:"subreddit_id"`
|
||||||
|
AuthorIsBlocked bool `json:"author_is_blocked"`
|
||||||
|
ModReasonBy interface{} `json:"mod_reason_by"`
|
||||||
|
NumReports interface{} `json:"num_reports"`
|
||||||
|
RemovalReason interface{} `json:"removal_reason"`
|
||||||
|
LinkFlairBackgroundColor string `json:"link_flair_background_color"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
IsRobotIndexable bool `json:"is_robot_indexable"`
|
||||||
|
ReportReasons interface{} `json:"report_reasons"`
|
||||||
|
Author string `json:"author"`
|
||||||
|
DiscussionType interface{} `json:"discussion_type"`
|
||||||
|
NumComments int `json:"num_comments"`
|
||||||
|
SendReplies bool `json:"send_replies"`
|
||||||
|
WhitelistStatus interface{} `json:"whitelist_status"`
|
||||||
|
ContestMode bool `json:"contest_mode"`
|
||||||
|
ModReports []interface{} `json:"mod_reports"`
|
||||||
|
AuthorPatreonFlair bool `json:"author_patreon_flair"`
|
||||||
|
AuthorFlairTextColor string `json:"author_flair_text_color"`
|
||||||
|
Permalink string `json:"permalink"`
|
||||||
|
ParentWhitelistStatus interface{} `json:"parent_whitelist_status"`
|
||||||
|
Stickied bool `json:"stickied"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
SubredditSubscribers int `json:"subreddit_subscribers"`
|
||||||
|
CreatedUtc float64 `json:"created_utc"`
|
||||||
|
NumCrossposts int `json:"num_crossposts"`
|
||||||
|
Media interface{} `json:"media"`
|
||||||
|
IsVideo bool `json:"is_video"`
|
||||||
|
} `json:"data"`
|
||||||
|
} `json:"children"`
|
||||||
|
Before interface{} `json:"before"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user