Basic routes have been added (#10)

* basic routes are working with db context

* swagger is working along with swag gen

* cron was updated with a class and better db context, untested though

* sourcelist command added

* lost the pg package but added it back

* Updated the api startup for cron and api

* updated source routes and started to add article routes

* Updated cron add func calls

* updated swagger

* keeping articles basic for now as I dont need to pull them in yet

* swagger update

* added getarticlesbysourceid call

* adding the subscriptions table to track who to send notifications and where

* removed legacy columns from discordwebhooks that are no longer needed.

* added discord webhook routes

* updated routes

* Minor change to schema

* Updated routes to support subscriptions

* ignore .vscode
This commit is contained in:
James Tombleson 2022-06-19 22:02:44 -07:00 committed by GitHub
parent 77d4fdf01a
commit 713205bb03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 2715 additions and 121 deletions

2
.gitignore vendored
View File

@ -1,6 +1,8 @@
.env
dev.session.sql
.vscode
# Binaries for programs and plugins
*.exe
*.exe~

View File

@ -33,7 +33,7 @@ INSERT INTO sources VALUES
-- Twitch Entries
INSERT INTO sources VALUES
(uuid_generate_v4(), 'twitch', 'Nintendo', 'twitch', 'api', 'a', TRUE, 'https://store.steampowered.com/feeds/news/app/1675200/?cc=US&l=english&snr=1_2108_9__2107', 'rss, steampowered, steam, deck, steam deck');
(uuid_generate_v4(), 'twitch', 'Nintendo', 'twitch', 'api', 'a', TRUE, 'https://twitch.tv/nintendo', 'twitch, nintendo');
-- +goose StatementEnd

View File

@ -0,0 +1,21 @@
-- +goose Up
-- +goose StatementBegin
SELECT 'up SQL query';
Create TABLE Subscriptions (
ID uuid Primary Key,
DiscordWebHookID uuid Not Null,
SourceID uuid Not Null
);
ALTER TABLE discordwebhooks drop COLUMN Name;
ALTER TABLE discordwebhooks drop COLUMN Key;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
SELECT 'down SQL query';
Drop Table Subscriptions;
ALTER TABLE discordwebhooks Add COLUMN Name TEXT;
--ALTER TABLE discordwebhooks Add COLUMN Key TEXT;
-- +goose StatementEnd

View File

@ -34,8 +34,6 @@ type Discordqueue struct {
type Discordwebhook struct {
ID uuid.UUID
Name string
Key sql.NullString
Url string
Server string
Channel string
@ -66,3 +64,9 @@ type Source struct {
Url string
Tags string
}
type Subscription struct {
ID uuid.UUID
Discordwebhookid uuid.UUID
Sourceid uuid.UUID
}

View File

@ -75,15 +75,13 @@ func (q *Queries) CreateDiscordQueue(ctx context.Context, arg CreateDiscordQueue
const createDiscordWebHook = `-- name: CreateDiscordWebHook :exec
Insert Into DiscordWebHooks
(ID, Name, Key, Url, Server, Channel, Enabled)
(ID, Url, Server, Channel, Enabled)
Values
($1, $2, $3, $4, $5, $6, $7)
($1, $2, $3, $4, $5)
`
type CreateDiscordWebHookParams struct {
ID uuid.UUID
Name string
Key sql.NullString
Url string
Server string
Channel string
@ -94,8 +92,6 @@ type CreateDiscordWebHookParams struct {
func (q *Queries) CreateDiscordWebHook(ctx context.Context, arg CreateDiscordWebHookParams) error {
_, err := q.db.ExecContext(ctx, createDiscordWebHook,
arg.ID,
arg.Name,
arg.Key,
arg.Url,
arg.Server,
arg.Channel,
@ -194,6 +190,23 @@ func (q *Queries) CreateSource(ctx context.Context, arg CreateSourceParams) erro
return err
}
const createSubscription = `-- name: CreateSubscription :exec
Insert Into subscriptions (ID, DiscordWebHookId, SourceId) Values ($1, $2, $3)
`
type CreateSubscriptionParams struct {
ID uuid.UUID
Discordwebhookid uuid.UUID
Sourceid uuid.UUID
}
// Subscriptions
func (q *Queries) CreateSubscription(ctx context.Context, arg CreateSubscriptionParams) error {
_, err := q.db.ExecContext(ctx, createSubscription, arg.ID, arg.Discordwebhookid, arg.Sourceid)
return err
}
const deleteDiscordQueueItem = `-- name: DeleteDiscordQueueItem :exec
Delete From DiscordQueue Where ID = $1
`
@ -239,6 +252,38 @@ func (q *Queries) DeleteSource(ctx context.Context, id uuid.UUID) error {
return err
}
const deleteSubscription = `-- name: DeleteSubscription :exec
Delete From subscriptions Where discordwebhookid = $1 and sourceid = $2
`
type DeleteSubscriptionParams struct {
Discordwebhookid uuid.UUID
Sourceid uuid.UUID
}
func (q *Queries) DeleteSubscription(ctx context.Context, arg DeleteSubscriptionParams) error {
_, err := q.db.ExecContext(ctx, deleteSubscription, arg.Discordwebhookid, arg.Sourceid)
return err
}
const disableSource = `-- name: DisableSource :exec
Update Sources Set Enabled = FALSE where ID = $1
`
func (q *Queries) DisableSource(ctx context.Context, id uuid.UUID) error {
_, err := q.db.ExecContext(ctx, disableSource, id)
return err
}
const enableSource = `-- name: EnableSource :exec
Update Sources Set Enabled = TRUE where ID = $1
`
func (q *Queries) EnableSource(ctx context.Context, id uuid.UUID) error {
_, err := q.db.ExecContext(ctx, enableSource, id)
return err
}
const getArticleByID = `-- name: GetArticleByID :one
Select id, sourceid, tags, title, url, pubdate, video, videoheight, videowidth, thumbnail, description, authorname, authorimage from Articles
WHERE ID = $1 LIMIT 1
@ -292,6 +337,191 @@ func (q *Queries) GetArticleByUrl(ctx context.Context, url string) (Article, err
return i, err
}
const getArticlesBySource = `-- name: GetArticlesBySource :many
select articles.id, sourceid, articles.tags, title, articles.url, pubdate, video, videoheight, videowidth, thumbnail, description, authorname, authorimage, sources.id, site, name, source, type, value, enabled, sources.url, sources.tags from articles
INNER join sources on articles.sourceid=Sources.ID
where site = $1
`
type GetArticlesBySourceRow struct {
ID uuid.UUID
Sourceid uuid.UUID
Tags string
Title string
Url string
Pubdate time.Time
Video sql.NullString
Videoheight int32
Videowidth int32
Thumbnail string
Description string
Authorname sql.NullString
Authorimage sql.NullString
ID_2 uuid.UUID
Site string
Name string
Source string
Type string
Value sql.NullString
Enabled bool
Url_2 string
Tags_2 string
}
func (q *Queries) GetArticlesBySource(ctx context.Context, site string) ([]GetArticlesBySourceRow, error) {
rows, err := q.db.QueryContext(ctx, getArticlesBySource, site)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetArticlesBySourceRow
for rows.Next() {
var i GetArticlesBySourceRow
if err := rows.Scan(
&i.ID,
&i.Sourceid,
&i.Tags,
&i.Title,
&i.Url,
&i.Pubdate,
&i.Video,
&i.Videoheight,
&i.Videowidth,
&i.Thumbnail,
&i.Description,
&i.Authorname,
&i.Authorimage,
&i.ID_2,
&i.Site,
&i.Name,
&i.Source,
&i.Type,
&i.Value,
&i.Enabled,
&i.Url_2,
&i.Tags_2,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getArticlesBySourceId = `-- name: GetArticlesBySourceId :many
Select id, sourceid, tags, title, url, pubdate, video, videoheight, videowidth, thumbnail, description, authorname, authorimage From articles
Where sourceid = $1 Limit 50
`
func (q *Queries) GetArticlesBySourceId(ctx context.Context, sourceid uuid.UUID) ([]Article, error) {
rows, err := q.db.QueryContext(ctx, getArticlesBySourceId, sourceid)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Article
for rows.Next() {
var i Article
if err := rows.Scan(
&i.ID,
&i.Sourceid,
&i.Tags,
&i.Title,
&i.Url,
&i.Pubdate,
&i.Video,
&i.Videoheight,
&i.Videowidth,
&i.Thumbnail,
&i.Description,
&i.Authorname,
&i.Authorimage,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getArticlesBySourceName = `-- name: GetArticlesBySourceName :many
select
articles.ID, articles.SourceId, articles.Tags, articles.Title, articles.Url, articles.PubDate, articles.Video, articles.VideoHeight, articles.VideoWidth, articles.Thumbnail, articles.Description, articles.AuthorName, articles.AuthorImage, sources.source, sources.name
From articles
Left Join sources
On articles.sourceid = sources.id
Where name = $1
`
type GetArticlesBySourceNameRow struct {
ID uuid.UUID
Sourceid uuid.UUID
Tags string
Title string
Url string
Pubdate time.Time
Video sql.NullString
Videoheight int32
Videowidth int32
Thumbnail string
Description string
Authorname sql.NullString
Authorimage sql.NullString
Source sql.NullString
Name sql.NullString
}
func (q *Queries) GetArticlesBySourceName(ctx context.Context, name string) ([]GetArticlesBySourceNameRow, error) {
rows, err := q.db.QueryContext(ctx, getArticlesBySourceName, name)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetArticlesBySourceNameRow
for rows.Next() {
var i GetArticlesBySourceNameRow
if err := rows.Scan(
&i.ID,
&i.Sourceid,
&i.Tags,
&i.Title,
&i.Url,
&i.Pubdate,
&i.Video,
&i.Videoheight,
&i.Videowidth,
&i.Thumbnail,
&i.Description,
&i.Authorname,
&i.Authorimage,
&i.Source,
&i.Name,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getDiscordQueueByID = `-- name: GetDiscordQueueByID :one
Select id, articleid from DiscordQueue
Where ID = $1 LIMIT 1
@ -332,7 +562,7 @@ func (q *Queries) GetDiscordQueueItems(ctx context.Context, limit int32) ([]Disc
}
const getDiscordWebHooksByID = `-- name: GetDiscordWebHooksByID :one
Select id, name, key, url, server, channel, enabled from DiscordWebHooks
Select id, url, server, channel, enabled from DiscordWebHooks
Where ID = $1 LIMIT 1
`
@ -341,8 +571,6 @@ func (q *Queries) GetDiscordWebHooksByID(ctx context.Context, id uuid.UUID) (Dis
var i Discordwebhook
err := row.Scan(
&i.ID,
&i.Name,
&i.Key,
&i.Url,
&i.Server,
&i.Channel,
@ -447,8 +675,103 @@ func (q *Queries) GetSourceByID(ctx context.Context, id uuid.UUID) (Source, erro
return i, err
}
const getSubscriptionsByDiscordWebHookId = `-- name: GetSubscriptionsByDiscordWebHookId :many
Select id, discordwebhookid, sourceid from subscriptions Where discordwebhookid = $1
`
func (q *Queries) GetSubscriptionsByDiscordWebHookId(ctx context.Context, discordwebhookid uuid.UUID) ([]Subscription, error) {
rows, err := q.db.QueryContext(ctx, getSubscriptionsByDiscordWebHookId, discordwebhookid)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Subscription
for rows.Next() {
var i Subscription
if err := rows.Scan(&i.ID, &i.Discordwebhookid, &i.Sourceid); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getSubscriptionsBySourceID = `-- name: GetSubscriptionsBySourceID :many
Select id, discordwebhookid, sourceid From subscriptions Where sourceid = $1
`
func (q *Queries) GetSubscriptionsBySourceID(ctx context.Context, sourceid uuid.UUID) ([]Subscription, error) {
rows, err := q.db.QueryContext(ctx, getSubscriptionsBySourceID, sourceid)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Subscription
for rows.Next() {
var i Subscription
if err := rows.Scan(&i.ID, &i.Discordwebhookid, &i.Sourceid); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listArticles = `-- name: ListArticles :many
Select id, sourceid, tags, title, url, pubdate, video, videoheight, videowidth, thumbnail, description, authorname, authorimage From articles Limit $1
`
func (q *Queries) ListArticles(ctx context.Context, limit int32) ([]Article, error) {
rows, err := q.db.QueryContext(ctx, listArticles, limit)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Article
for rows.Next() {
var i Article
if err := rows.Scan(
&i.ID,
&i.Sourceid,
&i.Tags,
&i.Title,
&i.Url,
&i.Pubdate,
&i.Video,
&i.Videoheight,
&i.Videowidth,
&i.Thumbnail,
&i.Description,
&i.Authorname,
&i.Authorimage,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listDiscordWebHooksByServer = `-- name: ListDiscordWebHooksByServer :many
Select id, name, key, url, server, channel, enabled From DiscordWebHooks
Select id, url, server, channel, enabled From DiscordWebHooks
Where Server = $1
`
@ -463,8 +786,6 @@ func (q *Queries) ListDiscordWebHooksByServer(ctx context.Context, server string
var i Discordwebhook
if err := rows.Scan(
&i.ID,
&i.Name,
&i.Key,
&i.Url,
&i.Server,
&i.Channel,
@ -483,6 +804,76 @@ func (q *Queries) ListDiscordWebHooksByServer(ctx context.Context, server string
return items, nil
}
const listDiscordWebhooks = `-- name: ListDiscordWebhooks :many
Select id, url, server, channel, enabled From discordwebhooks LIMIT $1
`
func (q *Queries) ListDiscordWebhooks(ctx context.Context, limit int32) ([]Discordwebhook, error) {
rows, err := q.db.QueryContext(ctx, listDiscordWebhooks, limit)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Discordwebhook
for rows.Next() {
var i Discordwebhook
if err := rows.Scan(
&i.ID,
&i.Url,
&i.Server,
&i.Channel,
&i.Enabled,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listSources = `-- name: ListSources :many
Select id, site, name, source, type, value, enabled, url, tags From Sources Limit $1
`
func (q *Queries) ListSources(ctx context.Context, limit int32) ([]Source, error) {
rows, err := q.db.QueryContext(ctx, listSources, limit)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Source
for rows.Next() {
var i Source
if err := rows.Scan(
&i.ID,
&i.Site,
&i.Name,
&i.Source,
&i.Type,
&i.Value,
&i.Enabled,
&i.Url,
&i.Tags,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listSourcesBySource = `-- name: ListSourcesBySource :many
Select id, site, name, source, type, value, enabled, url, tags From Sources where Source = $1
`
@ -519,3 +910,62 @@ func (q *Queries) ListSourcesBySource(ctx context.Context, source string) ([]Sou
}
return items, nil
}
const listSubscriptions = `-- name: ListSubscriptions :many
Select id, discordwebhookid, sourceid From subscriptions Limit $1
`
func (q *Queries) ListSubscriptions(ctx context.Context, limit int32) ([]Subscription, error) {
rows, err := q.db.QueryContext(ctx, listSubscriptions, limit)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Subscription
for rows.Next() {
var i Subscription
if err := rows.Scan(&i.ID, &i.Discordwebhookid, &i.Sourceid); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const querySubscriptions = `-- name: QuerySubscriptions :many
Select id, discordwebhookid, sourceid From subscriptions Where discordwebhookid = $1 and sourceid = $2
`
type QuerySubscriptionsParams struct {
Discordwebhookid uuid.UUID
Sourceid uuid.UUID
}
func (q *Queries) QuerySubscriptions(ctx context.Context, arg QuerySubscriptionsParams) ([]Subscription, error) {
rows, err := q.db.QueryContext(ctx, querySubscriptions, arg.Discordwebhookid, arg.Sourceid)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Subscription
for rows.Next() {
var i Subscription
if err := rows.Scan(&i.ID, &i.Discordwebhookid, &i.Sourceid); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}

View File

@ -7,6 +7,26 @@ WHERE ID = $1 LIMIT 1;
Select * from Articles
Where Url = $1 LIMIT 1;
-- name: ListArticles :many
Select * From articles Limit $1;
-- name: GetArticlesBySource :many
select * from articles
INNER join sources on articles.sourceid=Sources.ID
where site = $1;
-- name: GetArticlesBySourceId :many
Select * From articles
Where sourceid = $1 Limit 50;
-- name: GetArticlesBySourceName :many
select
articles.ID, articles.SourceId, articles.Tags, articles.Title, articles.Url, articles.PubDate, articles.Video, articles.VideoHeight, articles.VideoWidth, articles.Thumbnail, articles.Description, articles.AuthorName, articles.AuthorImage, sources.source, sources.name
From articles
Left Join sources
On articles.sourceid = sources.id
Where name = $1;
-- name: CreateArticle :exec
INSERT INTO Articles
(ID, SourceId, Tags, Title, Url, PubDate, Video, VideoHeight, VideoWidth, Thumbnail, Description, AuthorName, AuthorImage)
@ -31,13 +51,12 @@ Delete From DiscordQueue Where ID = $1;
-- name: GetDiscordQueueItems :many
Select * from DiscordQueue LIMIT $1;
/* DiscordWebHooks */
-- name: CreateDiscordWebHook :exec
Insert Into DiscordWebHooks
(ID, Name, Key, Url, Server, Channel, Enabled)
(ID, Url, Server, Channel, Enabled)
Values
($1, $2, $3, $4, $5, $6, $7);
($1, $2, $3, $4, $5);
-- name: GetDiscordWebHooksByID :one
Select * from DiscordWebHooks
@ -47,6 +66,9 @@ Where ID = $1 LIMIT 1;
Select * From DiscordWebHooks
Where Server = $1;
-- name: ListDiscordWebhooks :many
Select * From discordwebhooks LIMIT $1;
-- name: DeleteDiscordWebHooks :exec
Delete From discordwebhooks Where ID = $1;
@ -105,8 +127,38 @@ Values
-- name: GetSourceByID :one
Select * From Sources where ID = $1 Limit 1;
-- name: ListSources :many
Select * From Sources Limit $1;
-- name: ListSourcesBySource :many
Select * From Sources where Source = $1;
-- name: DeleteSource :exec
DELETE From sources where id = $1;
-- name: DisableSource :exec
Update Sources Set Enabled = FALSE where ID = $1;
-- name: EnableSource :exec
Update Sources Set Enabled = TRUE where ID = $1;
/* Subscriptions */
-- name: CreateSubscription :exec
Insert Into subscriptions (ID, DiscordWebHookId, SourceId) Values ($1, $2, $3);
-- name: ListSubscriptions :many
Select * From subscriptions Limit $1;
-- name: QuerySubscriptions :many
Select * From subscriptions Where discordwebhookid = $1 and sourceid = $2;
-- name: GetSubscriptionsBySourceID :many
Select * From subscriptions Where sourceid = $1;
-- name: GetSubscriptionsByDiscordWebHookId :many
Select * from subscriptions Where discordwebhookid = $1;
-- name: DeleteSubscription :exec
Delete From subscriptions Where discordwebhookid = $1 and sourceid = $2;

View File

@ -21,8 +21,6 @@ CREATE Table DiscordQueue (
CREATE Table DiscordWebHooks (
ID uuid PRIMARY KEY,
Name TEXT NOT NULL, -- Defines webhook purpose
Key TEXT,
Url TEXT NOT NULL, -- Webhook Url
Server TEXT NOT NULL, -- Defines the server its bound it. Used for refrence
Channel TEXT NOT NULL, -- Defines the channel its bound to. Used for refrence
@ -54,3 +52,9 @@ Create Table Sources (
Tags TEXT NOT NULL
);
/* This table is used to track what the Web Hook wants to have sent by Source */;
Create TABLE Subscriptions (
ID uuid Primary Key,
DiscordWebHookID uuid Not Null,
SourceID uuid Not Null
);

483
docs/docs.go Normal file
View File

@ -0,0 +1,483 @@
// Package docs GENERATED BY SWAG; DO NOT EDIT
// This file was generated by swaggo/swag
package docs
import "github.com/swaggo/swag"
const docTemplate = `{
"schemes": {{ marshal .Schemes }},
"swagger": "2.0",
"info": {
"description": "{{escape .Description}}",
"title": "{{.Title}}",
"contact": {},
"version": "{{.Version}}"
},
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/articles": {
"get": {
"produces": [
"application/json"
],
"tags": [
"articles"
],
"summary": "Lists the top 50 records",
"responses": {}
}
},
"/articles/by/sourceid/{id}": {
"get": {
"produces": [
"application/json"
],
"tags": [
"articles"
],
"summary": "Finds the articles based on the SourceID provided. Returns the top 50.",
"parameters": [
{
"type": "string",
"description": "Source ID UUID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {}
}
},
"/articles/{id}": {
"get": {
"produces": [
"application/json"
],
"tags": [
"articles"
],
"summary": "Returns an article based on defined ID.",
"parameters": [
{
"type": "string",
"description": "uuid",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {}
}
},
"/config/sources": {
"get": {
"produces": [
"application/json"
],
"tags": [
"config",
"source"
],
"summary": "Lists the top 50 records",
"responses": {}
}
},
"/config/sources/new/reddit": {
"post": {
"tags": [
"config",
"source",
"reddit"
],
"summary": "Creates a new reddit source to monitor.",
"parameters": [
{
"type": "string",
"description": "name",
"name": "name",
"in": "query",
"required": true
},
{
"type": "string",
"description": "url",
"name": "url",
"in": "query",
"required": true
}
],
"responses": {}
}
},
"/config/sources/new/twitch": {
"post": {
"tags": [
"config",
"source",
"twitch"
],
"summary": "Creates a new twitch source to monitor.",
"parameters": [
{
"type": "string",
"description": "name",
"name": "name",
"in": "query",
"required": true
},
{
"type": "string",
"description": "url",
"name": "url",
"in": "query",
"required": true
},
{
"type": "string",
"description": "tags",
"name": "tags",
"in": "query",
"required": true
}
],
"responses": {}
}
},
"/config/sources/new/youtube": {
"post": {
"tags": [
"config",
"source",
"youtube"
],
"summary": "Creates a new youtube source to monitor.",
"parameters": [
{
"type": "string",
"description": "name",
"name": "name",
"in": "query",
"required": true
},
{
"type": "string",
"description": "url",
"name": "url",
"in": "query",
"required": true
},
{
"type": "string",
"description": "tags",
"name": "tags",
"in": "query",
"required": true
}
],
"responses": {}
}
},
"/config/sources/{id}": {
"get": {
"produces": [
"application/json"
],
"tags": [
"config",
"source"
],
"summary": "Returns a single entity by ID",
"parameters": [
{
"type": "string",
"description": "uuid",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {}
},
"delete": {
"tags": [
"config",
"source"
],
"summary": "Deletes a record by ID.",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {}
}
},
"/config/sources/{id}/disable": {
"post": {
"tags": [
"config",
"source"
],
"summary": "Disables a source from processing.",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {}
}
},
"/config/sources/{id}/enable": {
"post": {
"tags": [
"config",
"source"
],
"summary": "Enables a source to continue processing.",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {}
}
},
"/discord/queue": {
"get": {
"produces": [
"application/json"
],
"tags": [
"debug",
"Discord",
"Queue"
],
"summary": "Returns the top 100 entries from the queue to be processed.",
"responses": {}
}
},
"/discord/webhooks": {
"get": {
"produces": [
"application/json"
],
"tags": [
"config",
"Discord",
"Webhooks"
],
"summary": "Returns the top 100 entries from the queue to be processed.",
"responses": {}
}
},
"/discord/webhooks/byId": {
"get": {
"produces": [
"application/json"
],
"tags": [
"config",
"Discord",
"Webhooks"
],
"summary": "Returns the top 100 entries from the queue to be processed.",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "query",
"required": true
}
],
"responses": {}
}
},
"/discord/webhooks/new": {
"post": {
"tags": [
"config",
"Discord",
"Webhooks"
],
"summary": "Creates a new record for a discord web hook to post data to.",
"parameters": [
{
"type": "string",
"description": "url",
"name": "url",
"in": "query",
"required": true
},
{
"type": "string",
"description": "Server name",
"name": "server",
"in": "query",
"required": true
},
{
"type": "string",
"description": "Channel name.",
"name": "channel",
"in": "query",
"required": true
}
],
"responses": {}
}
},
"/hello/{who}": {
"get": {
"produces": [
"text/plain"
],
"tags": [
"debug"
],
"summary": "Responds back with \"Hello x\" depending on param passed in.",
"parameters": [
{
"type": "string",
"description": "Who",
"name": "who",
"in": "path",
"required": true
}
],
"responses": {}
}
},
"/helloworld": {
"get": {
"produces": [
"text/plain"
],
"tags": [
"debug"
],
"summary": "Responds back with \"Hello world!\"",
"responses": {}
}
},
"/ping": {
"get": {
"produces": [
"text/plain"
],
"tags": [
"debug"
],
"summary": "Sends back \"pong\". Good to test with.",
"responses": {}
}
},
"/settings/{key}": {
"get": {
"produces": [
"application/json"
],
"tags": [
"settings"
],
"summary": "Returns a object based on the Key that was given/",
"parameters": [
{
"type": "string",
"description": "Settings Key value",
"name": "key",
"in": "path",
"required": true
}
],
"responses": {}
}
},
"/subscriptions": {
"get": {
"produces": [
"application/json"
],
"tags": [
"config",
"Subscriptions"
],
"summary": "Returns the top 100 entries from the queue to be processed.",
"responses": {}
}
},
"/subscriptions/byDiscordId": {
"get": {
"produces": [
"application/json"
],
"tags": [
"config",
"Subscriptions"
],
"summary": "Returns the top 100 entries from the queue to be processed.",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "query",
"required": true
}
],
"responses": {}
}
},
"/subscriptions/bySourceId": {
"get": {
"produces": [
"application/json"
],
"tags": [
"config",
"Subscriptions"
],
"summary": "Returns the top 100 entries from the queue to be processed.",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "query",
"required": true
}
],
"responses": {}
}
}
}
}`
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = &swag.Spec{
Version: "0.1",
Host: "",
BasePath: "/api",
Schemes: []string{},
Title: "NewsBot collector",
Description: "",
InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate,
}
func init() {
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
}

458
docs/swagger.json Normal file
View File

@ -0,0 +1,458 @@
{
"swagger": "2.0",
"info": {
"title": "NewsBot collector",
"contact": {},
"version": "0.1"
},
"basePath": "/api",
"paths": {
"/articles": {
"get": {
"produces": [
"application/json"
],
"tags": [
"articles"
],
"summary": "Lists the top 50 records",
"responses": {}
}
},
"/articles/by/sourceid/{id}": {
"get": {
"produces": [
"application/json"
],
"tags": [
"articles"
],
"summary": "Finds the articles based on the SourceID provided. Returns the top 50.",
"parameters": [
{
"type": "string",
"description": "Source ID UUID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {}
}
},
"/articles/{id}": {
"get": {
"produces": [
"application/json"
],
"tags": [
"articles"
],
"summary": "Returns an article based on defined ID.",
"parameters": [
{
"type": "string",
"description": "uuid",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {}
}
},
"/config/sources": {
"get": {
"produces": [
"application/json"
],
"tags": [
"config",
"source"
],
"summary": "Lists the top 50 records",
"responses": {}
}
},
"/config/sources/new/reddit": {
"post": {
"tags": [
"config",
"source",
"reddit"
],
"summary": "Creates a new reddit source to monitor.",
"parameters": [
{
"type": "string",
"description": "name",
"name": "name",
"in": "query",
"required": true
},
{
"type": "string",
"description": "url",
"name": "url",
"in": "query",
"required": true
}
],
"responses": {}
}
},
"/config/sources/new/twitch": {
"post": {
"tags": [
"config",
"source",
"twitch"
],
"summary": "Creates a new twitch source to monitor.",
"parameters": [
{
"type": "string",
"description": "name",
"name": "name",
"in": "query",
"required": true
},
{
"type": "string",
"description": "url",
"name": "url",
"in": "query",
"required": true
},
{
"type": "string",
"description": "tags",
"name": "tags",
"in": "query",
"required": true
}
],
"responses": {}
}
},
"/config/sources/new/youtube": {
"post": {
"tags": [
"config",
"source",
"youtube"
],
"summary": "Creates a new youtube source to monitor.",
"parameters": [
{
"type": "string",
"description": "name",
"name": "name",
"in": "query",
"required": true
},
{
"type": "string",
"description": "url",
"name": "url",
"in": "query",
"required": true
},
{
"type": "string",
"description": "tags",
"name": "tags",
"in": "query",
"required": true
}
],
"responses": {}
}
},
"/config/sources/{id}": {
"get": {
"produces": [
"application/json"
],
"tags": [
"config",
"source"
],
"summary": "Returns a single entity by ID",
"parameters": [
{
"type": "string",
"description": "uuid",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {}
},
"delete": {
"tags": [
"config",
"source"
],
"summary": "Deletes a record by ID.",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {}
}
},
"/config/sources/{id}/disable": {
"post": {
"tags": [
"config",
"source"
],
"summary": "Disables a source from processing.",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {}
}
},
"/config/sources/{id}/enable": {
"post": {
"tags": [
"config",
"source"
],
"summary": "Enables a source to continue processing.",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {}
}
},
"/discord/queue": {
"get": {
"produces": [
"application/json"
],
"tags": [
"debug",
"Discord",
"Queue"
],
"summary": "Returns the top 100 entries from the queue to be processed.",
"responses": {}
}
},
"/discord/webhooks": {
"get": {
"produces": [
"application/json"
],
"tags": [
"config",
"Discord",
"Webhooks"
],
"summary": "Returns the top 100 entries from the queue to be processed.",
"responses": {}
}
},
"/discord/webhooks/byId": {
"get": {
"produces": [
"application/json"
],
"tags": [
"config",
"Discord",
"Webhooks"
],
"summary": "Returns the top 100 entries from the queue to be processed.",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "query",
"required": true
}
],
"responses": {}
}
},
"/discord/webhooks/new": {
"post": {
"tags": [
"config",
"Discord",
"Webhooks"
],
"summary": "Creates a new record for a discord web hook to post data to.",
"parameters": [
{
"type": "string",
"description": "url",
"name": "url",
"in": "query",
"required": true
},
{
"type": "string",
"description": "Server name",
"name": "server",
"in": "query",
"required": true
},
{
"type": "string",
"description": "Channel name.",
"name": "channel",
"in": "query",
"required": true
}
],
"responses": {}
}
},
"/hello/{who}": {
"get": {
"produces": [
"text/plain"
],
"tags": [
"debug"
],
"summary": "Responds back with \"Hello x\" depending on param passed in.",
"parameters": [
{
"type": "string",
"description": "Who",
"name": "who",
"in": "path",
"required": true
}
],
"responses": {}
}
},
"/helloworld": {
"get": {
"produces": [
"text/plain"
],
"tags": [
"debug"
],
"summary": "Responds back with \"Hello world!\"",
"responses": {}
}
},
"/ping": {
"get": {
"produces": [
"text/plain"
],
"tags": [
"debug"
],
"summary": "Sends back \"pong\". Good to test with.",
"responses": {}
}
},
"/settings/{key}": {
"get": {
"produces": [
"application/json"
],
"tags": [
"settings"
],
"summary": "Returns a object based on the Key that was given/",
"parameters": [
{
"type": "string",
"description": "Settings Key value",
"name": "key",
"in": "path",
"required": true
}
],
"responses": {}
}
},
"/subscriptions": {
"get": {
"produces": [
"application/json"
],
"tags": [
"config",
"Subscriptions"
],
"summary": "Returns the top 100 entries from the queue to be processed.",
"responses": {}
}
},
"/subscriptions/byDiscordId": {
"get": {
"produces": [
"application/json"
],
"tags": [
"config",
"Subscriptions"
],
"summary": "Returns the top 100 entries from the queue to be processed.",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "query",
"required": true
}
],
"responses": {}
}
},
"/subscriptions/bySourceId": {
"get": {
"produces": [
"application/json"
],
"tags": [
"config",
"Subscriptions"
],
"summary": "Returns the top 100 entries from the queue to be processed.",
"parameters": [
{
"type": "string",
"description": "id",
"name": "id",
"in": "query",
"required": true
}
],
"responses": {}
}
}
}
}

316
docs/swagger.yaml Normal file
View File

@ -0,0 +1,316 @@
basePath: /api
info:
contact: {}
title: NewsBot collector
version: "0.1"
paths:
/articles:
get:
produces:
- application/json
responses: {}
summary: Lists the top 50 records
tags:
- articles
/articles/{id}:
get:
parameters:
- description: uuid
in: path
name: id
required: true
type: string
produces:
- application/json
responses: {}
summary: Returns an article based on defined ID.
tags:
- articles
/articles/by/sourceid/{id}:
get:
parameters:
- description: Source ID UUID
in: path
name: id
required: true
type: string
produces:
- application/json
responses: {}
summary: Finds the articles based on the SourceID provided. Returns the top
50.
tags:
- articles
/config/sources:
get:
produces:
- application/json
responses: {}
summary: Lists the top 50 records
tags:
- config
- source
/config/sources/{id}:
delete:
parameters:
- description: id
in: path
name: id
required: true
type: string
responses: {}
summary: Deletes a record by ID.
tags:
- config
- source
get:
parameters:
- description: uuid
in: path
name: id
required: true
type: string
produces:
- application/json
responses: {}
summary: Returns a single entity by ID
tags:
- config
- source
/config/sources/{id}/disable:
post:
parameters:
- description: id
in: path
name: id
required: true
type: string
responses: {}
summary: Disables a source from processing.
tags:
- config
- source
/config/sources/{id}/enable:
post:
parameters:
- description: id
in: path
name: id
required: true
type: string
responses: {}
summary: Enables a source to continue processing.
tags:
- config
- source
/config/sources/new/reddit:
post:
parameters:
- description: name
in: query
name: name
required: true
type: string
- description: url
in: query
name: url
required: true
type: string
responses: {}
summary: Creates a new reddit source to monitor.
tags:
- config
- source
- reddit
/config/sources/new/twitch:
post:
parameters:
- description: name
in: query
name: name
required: true
type: string
- description: url
in: query
name: url
required: true
type: string
- description: tags
in: query
name: tags
required: true
type: string
responses: {}
summary: Creates a new twitch source to monitor.
tags:
- config
- source
- twitch
/config/sources/new/youtube:
post:
parameters:
- description: name
in: query
name: name
required: true
type: string
- description: url
in: query
name: url
required: true
type: string
- description: tags
in: query
name: tags
required: true
type: string
responses: {}
summary: Creates a new youtube source to monitor.
tags:
- config
- source
- youtube
/discord/queue:
get:
produces:
- application/json
responses: {}
summary: Returns the top 100 entries from the queue to be processed.
tags:
- debug
- Discord
- Queue
/discord/webhooks:
get:
produces:
- application/json
responses: {}
summary: Returns the top 100 entries from the queue to be processed.
tags:
- config
- Discord
- Webhooks
/discord/webhooks/byId:
get:
parameters:
- description: id
in: query
name: id
required: true
type: string
produces:
- application/json
responses: {}
summary: Returns the top 100 entries from the queue to be processed.
tags:
- config
- Discord
- Webhooks
/discord/webhooks/new:
post:
parameters:
- description: url
in: query
name: url
required: true
type: string
- description: Server name
in: query
name: server
required: true
type: string
- description: Channel name.
in: query
name: channel
required: true
type: string
responses: {}
summary: Creates a new record for a discord web hook to post data to.
tags:
- config
- Discord
- Webhooks
/hello/{who}:
get:
parameters:
- description: Who
in: path
name: who
required: true
type: string
produces:
- text/plain
responses: {}
summary: Responds back with "Hello x" depending on param passed in.
tags:
- debug
/helloworld:
get:
produces:
- text/plain
responses: {}
summary: Responds back with "Hello world!"
tags:
- debug
/ping:
get:
produces:
- text/plain
responses: {}
summary: Sends back "pong". Good to test with.
tags:
- debug
/settings/{key}:
get:
parameters:
- description: Settings Key value
in: path
name: key
required: true
type: string
produces:
- application/json
responses: {}
summary: Returns a object based on the Key that was given/
tags:
- settings
/subscriptions:
get:
produces:
- application/json
responses: {}
summary: Returns the top 100 entries from the queue to be processed.
tags:
- config
- Subscriptions
/subscriptions/byDiscordId:
get:
parameters:
- description: id
in: query
name: id
required: true
type: string
produces:
- application/json
responses: {}
summary: Returns the top 100 entries from the queue to be processed.
tags:
- config
- Subscriptions
/subscriptions/bySourceId:
get:
parameters:
- description: id
in: query
name: id
required: true
type: string
produces:
- application/json
responses: {}
summary: Returns the top 100 entries from the queue to be processed.
tags:
- config
- Subscriptions
swagger: "2.0"

2
go.mod
View File

@ -8,7 +8,6 @@ require (
github.com/go-rod/rod v0.107.1
github.com/google/uuid v1.3.0
github.com/joho/godotenv v1.4.0
github.com/lib/pq v1.10.6
github.com/mmcdole/gofeed v1.1.3
github.com/nicklaw5/helix/v2 v2.4.0
github.com/robfig/cron/v3 v3.0.1
@ -26,6 +25,7 @@ require (
github.com/golang-jwt/jwt/v4 v4.4.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/lib/pq v1.10.6
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mmcdole/goxpp v0.0.0-20200921145534-2f3784f67354 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect

20
go.sum
View File

@ -25,8 +25,6 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU=
github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-rod/rod v0.105.1 h1:r0bNmO9siOe13lG6Vbkaak11u48rYmWGl/Hk4MJdOiE=
github.com/go-rod/rod v0.105.1/go.mod h1:Wrnn6HokFHskwaIVke3ML1y/NBVp7XPIeB8eDzR9vuw=
github.com/go-rod/rod v0.107.1 h1:wRxTTAXJ0JUnoSGcyGAOubpdrToWIKPCnLu3av8EDFY=
github.com/go-rod/rod v0.107.1/go.mod h1:Au6ufsz7KyXUJVnw6Ljs1nFpsopy+9AJ/lBwGauYBVg=
github.com/golang-jwt/jwt/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kUcQ=
@ -38,7 +36,6 @@ github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
@ -56,15 +53,12 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mmcdole/gofeed v1.1.3 h1:pdrvMb18jMSLidGp8j0pLvc9IGziX4vbmvVqmLH6z8o=
github.com/mmcdole/gofeed v1.1.3/go.mod h1:QQO3maftbOu+hiVOGOZDRLymqGQCos4zxbA4j89gMrE=
github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf h1:sWGE2v+hO0Nd4yFU/S/mDBM5plIU8v/Qhfz41hkDIAI=
github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf/go.mod h1:pasqhqstspkosTneA62Nc+2p9SOBBYAPbnmRRWPQ0V8=
github.com/mmcdole/goxpp v0.0.0-20200921145534-2f3784f67354 h1:Z6i7ND25ixRtXFBylIUggqpvLMV1I15yprcqMVB7WZA=
github.com/mmcdole/goxpp v0.0.0-20200921145534-2f3784f67354/go.mod h1:pasqhqstspkosTneA62Nc+2p9SOBBYAPbnmRRWPQ0V8=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
@ -82,12 +76,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2 h1:+iNTcqQJy0OZ5jk6a5NLib47eqXK8uYcPX+O4+cBpEM=
github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w=
github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe h1:K8pHPVoTgxFJt1lXuIzzOX7zZhZFldJQK/CgKx9BFIc=
github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w=
github.com/swaggo/http-swagger v1.2.8 h1:TVjxLU7qoqofJ9qynJazmpTGs/p4Kx9FTp7YYwOkJb0=
github.com/swaggo/http-swagger v1.2.8/go.mod h1:FrQwV7rx+A5t11PIX8d+tFJa2GKx11RdAXQptllPQHg=
github.com/swaggo/http-swagger v1.3.0 h1:1+6M4qRorIbdyTWTsGrwnb0r9jGK5dcWN82O6oY/yHQ=
github.com/swaggo/http-swagger v1.3.0/go.mod h1:9glekdg40lwclrrKNRGgj/IMDxpNPZ3kzab4oPcF8EM=
github.com/swaggo/swag v1.8.2 h1:D4aBiVS2a65zhyk3WFqOUz7Rz0sOaUcgeErcid5uGL4=
@ -95,20 +85,17 @@ github.com/swaggo/swag v1.8.2/go.mod h1:jMLeXOOmYyjk8PvHTsXBdrubsNd9gUJTTCzL5iBn
github.com/urfave/cli v1.22.3/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ=
github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18=
github.com/ysmood/got v0.23.2 h1:U2U0vyQ/gDaawkKJZK/hyza8UUXbWCurbmazK7AcAfY=
github.com/ysmood/got v0.23.2/go.mod h1:pE1l4LOwOBhQg6A/8IAatkGp7uZjnalzrZolnlhhMgY=
github.com/ysmood/got v0.29.5 h1:+wMnm8UjoyYFMfeAsr57a1bahWTkloysc0Hxsu2gmnM=
github.com/ysmood/got v0.29.5/go.mod h1:pE1l4LOwOBhQg6A/8IAatkGp7uZjnalzrZolnlhhMgY=
github.com/ysmood/gotrace v0.6.0 h1:SyI1d4jclswLhg7SWTL6os3L1WOKeNn/ZtzVQF8QmdY=
github.com/ysmood/gotrace v0.6.0/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM=
github.com/ysmood/gson v0.7.1 h1:zKL2MTGtynxdBdlZjyGsvEOZ7dkxaY5TH6QhAbTgz0Q=
github.com/ysmood/gson v0.7.1/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg=
github.com/ysmood/gson v0.7.2 h1:1iWUvpi5DPvd2j59W7ifRPR9DiAZ3Ga+fmMl1mJrRbM=
github.com/ysmood/gson v0.7.2/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg=
github.com/ysmood/leakless v0.7.0 h1:XCGdaPExyoreoQd+H5qgxM3ReNbSPFsEXpSKwbXbwQw=
github.com/ysmood/leakless v0.7.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -119,8 +106,6 @@ golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 h1:z8Hj/bl9cOV2grsOpEaQFUaly0JWN3i97mo3jXKJNp0=
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220614162138-6c1b26c55098 h1:PgOr27OhUx2IRqGJ2RxAWI4dJQ7bi9cSrB82uzFzfUA=
golang.org/x/sys v0.0.0-20220614162138-6c1b26c55098/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@ -130,11 +115,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=

32
main.go
View File

@ -2,31 +2,29 @@ package main
import (
"context"
"log"
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
_ "github.com/jtom38/newsbot/collector/docs"
"github.com/jtom38/newsbot/collector/routes"
"github.com/jtom38/newsbot/collector/services/cron"
)
// @title NewsBot collector
// @version 0.1
// @BasePath /api
func main() {
ctx := context.Background()
c := cron.New(ctx)
c.Start()
cron.EnableScheduler(ctx)
server := routes.NewServer(ctx)
app := chi.NewRouter()
app.Use(middleware.Logger)
app.Use(middleware.Recoverer)
//app.Mount("/swagger", httpSwagger.WrapHandler)
app.Mount("/api", routes.RootRoutes())
log.Println("API is online and waiting for requests.")
log.Println("API: http://localhost:8081/api")
//log.Println("Swagger: http://localhost:8080/swagger/index.html")
err := http.ListenAndServe(":8081", app)
if err != nil { log.Fatalln(err) }
fmt.Println("API is online and waiting for requests.")
fmt.Println("API: http://localhost:8081/api")
fmt.Println("Swagger: http://localhost:8081/swagger/index.html")
err := http.ListenAndServe(":8081", server.Router)
if err != nil { panic(err) }
}

View File

@ -3,6 +3,9 @@ help: ## Shows this help command
@egrep -h '\s##\s' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
build: ## builds the application with the current go runtime
sqlc generate
~/go/bin/swag f
~/go/bin/swag i
go build .
docker-build: ## Generates the docker image
@ -14,3 +17,10 @@ migrate-dev: ## Apply sql migrations to dev db
migrate-dev-down: ## revert sql migrations to dev db
goose -dir "./database/migrations" postgres "user=postgres password=postgres dbname=postgres sslmode=disable" down
swag: ## Generates the swagger documentation with the swag tool
~/go/bin/swag f
~/go/bin/swag i
gensql: ## Generates SQL code with sqlc
sqlc generate

95
routes/articles.go Normal file
View File

@ -0,0 +1,95 @@
package routes
import (
"encoding/json"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/google/uuid"
)
// ListArticles
// @Summary Lists the top 50 records
// @Produce application/json
// @Tags articles
// @Router /articles [get]
func (s *Server) listArticles(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
res, err := s.Db.ListArticles(*s.ctx, 50)
if err != nil {
w.Write([]byte(err.Error()))
panic(err)
}
bres, err := json.Marshal(res)
if err != nil {
w.Write([]byte(err.Error()))
panic(err)
}
w.Write(bres)
}
// GetArticleById
// @Summary Returns an article based on defined ID.
// @Param id path string true "uuid"
// @Produce application/json
// @Tags articles
// @Router /articles/{id} [get]
func (s *Server) getArticleById(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
id := chi.URLParam(r, "ID")
uuid, err := uuid.Parse(id)
if err != nil {
w.Write([]byte(err.Error()))
panic(err)
}
res, err := s.Db.GetArticleByID(*s.ctx, uuid)
if err != nil {
w.Write([]byte(err.Error()))
panic(err)
}
bres, err := json.Marshal(res)
if err != nil {
w.Write([]byte(err.Error()))
panic(err)
}
w.Write(bres)
}
// TODO add page support
// GetArticlesBySourceID
// @Summary Finds the articles based on the SourceID provided. Returns the top 50.
// @Param id path string true "Source ID UUID"
// @Produce application/json
// @Tags articles
// @Router /articles/by/sourceid/{id} [get]
func (s *Server) GetArticlesBySourceId(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
id := chi.URLParam(r, "ID")
uuid, err := uuid.Parse(id)
if err != nil {
w.Write([]byte(err.Error()))
panic(err)
}
res, err := s.Db.GetArticlesBySourceId(*s.ctx, uuid)
if err != nil {
w.Write([]byte(err.Error()))
panic(err)
}
bres, err := json.Marshal(res)
if err != nil {
w.Write([]byte(err.Error()))
panic(err)
}
w.Write(bres)
}

29
routes/discordQueue.go Normal file
View File

@ -0,0 +1,29 @@
package routes
import (
"encoding/json"
"net/http"
)
// GetDiscordQueue
// @Summary Returns the top 100 entries from the queue to be processed.
// @Produce application/json
// @Tags debug, Discord, Queue
// @Router /discord/queue [get]
func (s *Server) GetDiscordQueue(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
res, err := s.Db.GetDiscordQueueItems(*s.ctx, 100)
if err != nil {
w.Write([]byte(err.Error()))
panic(err)
}
bres, err := json.Marshal(res)
if err != nil {
w.Write([]byte(err.Error()))
panic(err)
}
w.Write(bres)
}

115
routes/discordwebhooks.go Normal file
View File

@ -0,0 +1,115 @@
package routes
import (
"encoding/json"
"log"
"net/http"
"strings"
"github.com/google/uuid"
"github.com/jtom38/newsbot/collector/database"
)
// GetDiscordWebHooks
// @Summary Returns the top 100 entries from the queue to be processed.
// @Produce application/json
// @Tags config, Discord, Webhooks
// @Router /discord/webhooks [get]
func (s *Server) GetDiscordWebHooks(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
res, err := s.Db.ListDiscordWebhooks(*s.ctx, 100)
if err != nil {
w.Write([]byte(err.Error()))
panic(err)
}
bres, err := json.Marshal(res)
if err != nil {
w.Write([]byte(err.Error()))
panic(err)
}
w.Write(bres)
}
// GetDiscorWebHooksById
// @Summary Returns the top 100 entries from the queue to be processed.
// @Produce application/json
// @Param id query string true "id"
// @Tags config, Discord, Webhooks
// @Router /discord/webhooks/byId [get]
func (s *Server) GetDiscordWebHooksById(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
query := r.URL.Query()
_id := query["id"][0]
if _id == "" {
http.Error(w, "id is missing", http.StatusBadRequest)
return
}
uuid, err := uuid.Parse(_id)
if err != nil {
http.Error(w, "unable to parse id value", http.StatusBadRequest)
return
}
res, err := s.Db.GetDiscordWebHooksByID(*s.ctx, uuid)
if err != nil {
http.Error(w, "no record found", http.StatusBadRequest)
return
}
bres, err := json.Marshal(res)
if err != nil {
http.Error(w, "unable to convert to json", http.StatusBadRequest)
panic(err)
}
w.Write(bres)
}
// NewDiscordWebHook
// @Summary Creates a new record for a discord web hook to post data to.
// @Param url query string true "url"
// @Param server query string true "Server name"
// @Param channel query string true "Channel name."
// @Tags config, Discord, Webhooks
// @Router /discord/webhooks/new [post]
func (s *Server) NewDiscordWebHook(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
_url := query["url"][0]
_server := query["server"][0]
_channel := query["channel"][0]
if _url == "" {
http.Error(w, "url is missing a value", http.StatusBadRequest)
return
}
if !strings.Contains(_url, "discord.com/api/webhooks") {
http.Error(w, "invalid url", http.StatusBadRequest)
return
}
if _server == ""{
http.Error(w, "server is missing", http.StatusBadRequest)
}
if _channel == "" {
http.Error(w, "channel is missing", http.StatusBadRequest)
}
params := database.CreateDiscordWebHookParams{
ID: uuid.New(),
Url: _url,
Server: _server,
Channel: _channel,
Enabled: true,
}
s.Db.CreateDiscordWebHook(*s.ctx, params)
bJson, err := json.Marshal(&params)
if err != nil {
log.Panicln(err)
}
w.Header().Set("Content-Type", "application/json")
w.Write(bJson)
}

View File

@ -1,25 +1,50 @@
package routes
import (
"net/http"
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
)
func RootRoutes() chi.Router {
app := chi.NewRouter()
app.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World!"))
app.Route("/", func(r chi.Router) {
r.Get("/helloworld", helloWorld)
r.Get("/ping", ping)
r.Route("/hello/{who}", func(r chi.Router) {
r.Get("/", helloWho)
})
app.Get("/ping", func(w http.ResponseWriter, r *http.Request) {
msg := "pong"
w.Write([]byte(msg))
})
app.Get("/hello/{world}", func(w http.ResponseWriter, r *http.Request) {
msg := fmt.Sprintf("Hello %v", chi.URLParam(r, "world"))
w.Write([]byte(msg))
})
return app
}
// HelloWorld
// @Summary Responds back with "Hello world!"
// @Produce plain
// @Tags debug
// @Router /helloworld [get]
func helloWorld(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World!"))
}
// Ping
// @Summary Sends back "pong". Good to test with.
// @Produce plain
// @Tags debug
// @Router /ping [get]
func ping(w http.ResponseWriter, r *http.Request) {
msg := "pong"
w.Write([]byte(msg))
}
// HelloWho
// @Summary Responds back with "Hello x" depending on param passed in.
// @Param who path string true "Who"
// @Produce plain
// @Tags debug
// @Router /hello/{who} [get]
func helloWho(w http.ResponseWriter, r *http.Request) {
msg := fmt.Sprintf("Hello %v", chi.URLParam(r, "who"))
w.Write([]byte(msg))
}

2
routes/root_test.go Normal file
View File

@ -0,0 +1,2 @@
package routes_test

108
routes/server.go Normal file
View File

@ -0,0 +1,108 @@
package routes
import (
"context"
"database/sql"
//"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
httpSwagger "github.com/swaggo/http-swagger"
_ "github.com/lib/pq"
"github.com/jtom38/newsbot/collector/database"
"github.com/jtom38/newsbot/collector/services/config"
)
type Server struct {
Router *chi.Mux
Db *database.Queries
ctx *context.Context
}
var (
ErrIdValueMissing string = "id value is missing"
ErrValueNotUuid string = "a value given was expected to be a uuid but was not correct."
ErrNoRecordFound string = "no record was found."
ErrUnableToConvertToJson string = "Unable to convert to json"
)
func NewServer(ctx context.Context) *Server {
s := &Server{
ctx: &ctx,
}
db, err := openDatabase(ctx)
if err != nil {
panic(err)
}
s.Db = db
s.Router = chi.NewRouter()
s.MountMiddleware()
s.MountRoutes()
return s
}
func openDatabase(ctx context.Context) (*database.Queries, error) {
_env := config.New()
connString := _env.GetConfig(config.Sql_Connection_String)
db, err := sql.Open("postgres", connString)
if err != nil {
panic(err)
}
queries := database.New(db)
return queries, err
}
func (s *Server) MountMiddleware() {
s.Router.Use(middleware.Logger)
s.Router.Use(middleware.Recoverer)
}
func (s *Server) MountRoutes() {
s.Router.Get("/swagger/*", httpSwagger.Handler(
httpSwagger.URL("http://localhost:8081/swagger/doc.json"), //The url pointing to API definition
))
/* Root Routes */
s.Router.Get("/api/helloworld", helloWorld)
s.Router.Get("/api/hello/{who}", helloWho)
s.Router.Get("/api/ping", ping)
/* Article Routes */
s.Router.Get("/api/articles", s.listArticles)
s.Router.Route("/api/articles/{ID}", func(r chi.Router) {
r.Get("/", s.getArticleById)
})
s.Router.Get("/api/articles/by/sourceid", s.GetArticlesBySourceId)
/* Discord Queue */
s.Router.Get("/api/discord/queue", s.GetDiscordQueue)
/* Discord WebHooks */
s.Router.Post("/api/discord/webhooks/new", s.NewDiscordWebHook)
s.Router.Get("/api/discord/webhooks", s.GetDiscordWebHooks)
s.Router.Get("/api/discord/webhooks/byId", s.GetDiscordWebHooksById)
/* Settings */
s.Router.Get("/api/settings", s.getSettings)
/* Source Routes */
s.Router.Get("/api/config/sources", s.listSources)
s.Router.Post("/api/config/sources/new/reddit", s.newRedditSource)
s.Router.Post("/api/config/sources/new/youtube", s.newYoutubeSource)
s.Router.Post("/api/config/sources/new/twitch", s.newTwitchSource)
s.Router.Route("/api/config/sources/{ID}", func(r chi.Router) {
r.Get("/", s.getSources)
r.Delete("/", s.deleteSources)
r.Post("/disable", s.disableSource)
r.Post("/enable", s.enableSource)
})
/* Subscriptions */
s.Router.Get("/api/subscriptions", s.ListSubscriptions)
s.Router.Get("/api/subscriptions/byDiscordId", s.GetSubscriptionsByDiscordId)
s.Router.Get("/api/subscriptions/bySourceId", s.GetSubscriptionsBySourceId)
}

44
routes/settings.go Normal file
View File

@ -0,0 +1,44 @@
package routes
import (
"encoding/json"
"log"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/google/uuid"
)
// GetSettings
// @Summary Returns a object based on the Key that was given/
// @Param key path string true "Settings Key value"
// @Produce application/json
// @Tags settings
// @Router /settings/{key} [get]
func (s *Server) getSettings(w http.ResponseWriter, r *http.Request) {
//var item model.Sources
id := chi.URLParam(r, "ID")
uuid, err := uuid.Parse(id)
if err != nil {
panic(err)
}
res, err := s.Db.GetSourceByID(*s.ctx, uuid)
if err != nil {
panic(err)
}
//itemId := fmt.Sprint(item.ID)
//if id != itemId {
// log.Panicln("Unable to find the requested record. Either unable to access SQL or the record does not exist.")
//}
bResult, err := json.Marshal(res)
if err != nil {
log.Panicln(err)
}
w.Header().Set("Content-Type", "application/json")
w.Write(bResult)
}

278
routes/sources.go Normal file
View File

@ -0,0 +1,278 @@
package routes
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strings"
"github.com/go-chi/chi/v5"
"github.com/google/uuid"
"github.com/jtom38/newsbot/collector/database"
)
// ListSources
// @Summary Lists the top 50 records
// @Produce application/json
// @Tags config, source
// @Router /config/sources [get]
func (s *Server) listSources(w http.ResponseWriter, r *http.Request) {
//TODO Add top?
/*
top := chi.URLParam(r, "top")
topInt, err := strconv.ParseInt(top, 0, 32)
if err != nil {
panic(err)
}
res, err := s.Db.ListSources(*s.ctx, int32(topInt))
*/
res, err := s.Db.ListSources(*s.ctx, 50)
if err != nil {
http.Error(w, "url is missing a value", http.StatusBadRequest)
return
}
bResult, err := json.Marshal(res)
if err != nil {
http.Error(w, "unable to convert to json", http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(bResult)
}
// GetSource
// @Summary Returns a single entity by ID
// @Param id path string true "uuid"
// @Produce application/json
// @Tags config, source
// @Router /config/sources/{id} [get]
func (s *Server) getSources(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "ID")
uuid, err := uuid.Parse(id)
if err != nil {
http.Error(w, "id is not a uuid", http.StatusBadRequest)
return
}
res, err := s.Db.GetSourceByID(*s.ctx, uuid)
if err != nil {
http.Error(w, "invalid id was given", http.StatusBadRequest)
panic(err)
}
bResult, err := json.Marshal(res)
if err != nil {
log.Panicln(err)
}
w.Header().Set("Content-Type", "application/json")
w.Write(bResult)
}
// NewRedditSource
// @Summary Creates a new reddit source to monitor.
// @Param name query string true "name"
// @Param url query string true "url"
// @Tags config, source, reddit
// @Router /config/sources/new/reddit [post]
func (s *Server) newRedditSource(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
_name := query["name"][0]
_url := query["url"][0]
_tags := query["tags"][0]
if _url == "" {
http.Error(w, "url is missing a value", http.StatusBadRequest)
return
}
if !strings.Contains(_url, "reddit.com") {
http.Error(w, "invalid url", http.StatusBadRequest)
return
}
tags := fmt.Sprintf("reddit, %v, %v", _name, _tags)
params := database.CreateSourceParams{
ID: uuid.New(),
Site: "reddit",
Name: _name,
Source: "reddit",
Type: "feed",
Enabled: true,
Url: _url,
Tags: tags,
}
s.Db.CreateSource(*s.ctx, params)
bJson, err := json.Marshal(&params)
if err != nil {
log.Panicln(err)
}
w.Header().Set("Content-Type", "application/json")
w.Write(bJson)
}
// NewYoutubeSource
// @Summary Creates a new youtube source to monitor.
// @Param name query string true "name"
// @Param url query string true "url"
// @Param tags query string true "tags"
// @Tags config, source, youtube
// @Router /config/sources/new/youtube [post]
func (s *Server) newYoutubeSource(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
_name := query["name"][0]
_url := query["url"][0]
_tags := query["tags"][0]
if _url == "" {
http.Error(w, "url is missing a value", http.StatusBadRequest)
return
}
if !strings.Contains(_url, "youtube.com") {
http.Error(w, "invalid url", http.StatusBadRequest)
return
}
tags := fmt.Sprintf("youtube, %v, %v", _name, _tags)
params := database.CreateSourceParams{
ID: uuid.New(),
Site: "youtube",
Name: _name,
Source: "youtube",
Type: "feed",
Enabled: true,
Url: _url,
Tags: tags,
}
s.Db.CreateSource(*s.ctx, params)
bJson, err := json.Marshal(&params)
if err != nil {
log.Panicln(err)
}
w.Header().Set("Content-Type", "application/json")
w.Write(bJson)
}
// NewTwitchSource
// @Summary Creates a new twitch source to monitor.
// @Param name query string true "name"
// @Param url query string true "url"
// @Param tags query string true "tags"
// @Tags config, source, twitch
// @Router /config/sources/new/twitch [post]
func (s *Server) newTwitchSource(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
_name := query["name"][0]
_url := query["url"][0]
_tags := query["tags"][0]
if _url == "" {
http.Error(w, "url is missing a value", http.StatusBadRequest)
return
}
if !strings.Contains(_url, "twitch.tv") {
http.Error(w, "invalid url", http.StatusBadRequest)
return
}
tags := fmt.Sprintf("twitch, %v, %v", _name, _tags)
params := database.CreateSourceParams{
ID: uuid.New(),
Site: "twitch",
Name: _name,
Source: "twitch",
Type: "api",
Enabled: true,
Url: _url,
Tags: tags,
}
s.Db.CreateSource(*s.ctx, params)
bJson, err := json.Marshal(&params)
if err != nil {
log.Panicln(err)
}
w.Header().Set("Content-Type", "application/json")
w.Write(bJson)
}
// DeleteSource
// @Summary Deletes a record by ID.
// @Param id path string true "id"
// @Tags config, source
// @Router /config/sources/{id} [delete]
func (s *Server) deleteSources(w http.ResponseWriter, r *http.Request) {
//var item model.Sources = model.Sources{}
id := chi.URLParam(r, "ID")
uuid, err := uuid.Parse(id)
if err != nil {
log.Panicln(err)
}
// Check to make sure we can find the record
_, err = s.Db.GetSourceByID(*s.ctx, uuid)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
}
// Delete the record
err = s.Db.DeleteSource(*s.ctx, uuid)
if err != nil {
log.Panic(err)
}
}
// DisableSource
// @Summary Disables a source from processing.
// @Param id path string true "id"
// @Tags config, source
// @Router /config/sources/{id}/disable [post]
func (s *Server) disableSource(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "ID")
uuid, err := uuid.Parse(id)
if err != nil {
log.Panicln(err)
}
// Check to make sure we can find the record
_, err = s.Db.GetSourceByID(*s.ctx, uuid)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
}
err = s.Db.DisableSource(*s.ctx, uuid)
if err != nil {
log.Panic(err)
}
}
// EnableSource
// @Summary Enables a source to continue processing.
// @Param id path string true "id"
// @Tags config, source
// @Router /config/sources/{id}/enable [post]
func (s *Server) enableSource(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "ID")
uuid, err := uuid.Parse(id)
if err != nil {
log.Panicln(err)
}
// Check to make sure we can find the record
_, err = s.Db.GetSourceByID(*s.ctx, uuid)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
}
err = s.Db.EnableSource(*s.ctx, uuid)
if err != nil {
log.Panic(err)
}
}

105
routes/subscriptions.go Normal file
View File

@ -0,0 +1,105 @@
package routes
import (
"encoding/json"
"net/http"
"github.com/google/uuid"
)
// GetSubscriptions
// @Summary Returns the top 100 entries from the queue to be processed.
// @Produce application/json
// @Tags config, Subscriptions
// @Router /subscriptions [get]
func (s *Server) ListSubscriptions(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
res, err := s.Db.ListSubscriptions(*s.ctx, 100)
if err != nil {
w.Write([]byte(err.Error()))
panic(err)
}
bres, err := json.Marshal(res)
if err != nil {
http.Error(w, ErrUnableToConvertToJson, http.StatusBadRequest)
panic(err)
}
w.Write(bres)
}
// GetSubscriptionsByDiscordId
// @Summary Returns the top 100 entries from the queue to be processed.
// @Produce application/json
// @Param id query string true "id"
// @Tags config, Subscriptions
// @Router /subscriptions/byDiscordId [get]
func (s *Server) GetSubscriptionsByDiscordId(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
query := r.URL.Query()
_id := query["id"][0]
if _id == "" {
http.Error(w, ErrIdValueMissing, http.StatusBadRequest)
return
}
uuid, err := uuid.Parse(_id)
if err != nil {
http.Error(w, ErrValueNotUuid, http.StatusBadRequest)
return
}
res, err := s.Db.GetSubscriptionsByDiscordWebHookId(*s.ctx, uuid)
if err != nil {
http.Error(w, ErrNoRecordFound, http.StatusBadRequest)
return
}
bres, err := json.Marshal(res)
if err != nil {
http.Error(w, ErrUnableToConvertToJson, http.StatusBadRequest)
return
}
w.Write(bres)
}
// GetSubscriptionsBySourceId
// @Summary Returns the top 100 entries from the queue to be processed.
// @Produce application/json
// @Param id query string true "id"
// @Tags config, Subscriptions
// @Router /subscriptions/bySourceId [get]
func (s *Server) GetSubscriptionsBySourceId(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
query := r.URL.Query()
_id := query["id"][0]
if _id == "" {
http.Error(w, ErrIdValueMissing, http.StatusBadRequest)
return
}
uuid, err := uuid.Parse(_id)
if err != nil {
http.Error(w, ErrValueNotUuid, http.StatusBadRequest)
return
}
res, err := s.Db.GetSubscriptionsByDiscordWebHookId(*s.ctx, uuid)
if err != nil {
http.Error(w, ErrNoRecordFound, http.StatusBadRequest)
return
}
bres, err := json.Marshal(res)
if err != nil {
http.Error(w, ErrUnableToConvertToJson, http.StatusBadRequest)
return
}
w.Write(bres)
}

View File

@ -15,40 +15,56 @@ import (
"github.com/jtom38/newsbot/collector/services/config"
)
var _env config.ConfigClient
var _connString string
var _queries *database.Queries
func EnableScheduler(ctx context.Context) {
c := cron.New()
OpenDatabase(ctx)
//c.AddFunc("*/5 * * * *", func() { go CheckCache() })
c.AddFunc("* */1 * * *", func() { go CheckReddit(ctx) })
//c.AddFunc("* */1 * * *", func() { go CheckYoutube() })
//c.AddFunc("* */1 * * *", func() { go CheckFfxiv() })
//c.AddFunc("* */1 * * *", func() { go CheckTwitch() })
c.Start()
type Cron struct {
Db *database.Queries
ctx *context.Context
timer *cron.Cron
}
// Open the connection to the database and share it with the package so all of them are able to share.
func OpenDatabase(ctx context.Context) error {
_env = config.New()
_connString = _env.GetConfig(config.Sql_Connection_String)
db, err := sql.Open("postgres", _connString)
func openDatabase() (*database.Queries, error) {
_env := config.New()
connString := _env.GetConfig(config.Sql_Connection_String)
db, err := sql.Open("postgres", connString)
if err != nil {
panic(err)
}
queries := database.New(db)
_queries = queries
return err
return queries, err
}
func New(ctx context.Context) *Cron {
c := &Cron{
ctx: &ctx,
}
timer := cron.New()
queries, err := openDatabase()
if err != nil {
panic(err)
}
c.Db = queries
//timer.AddFunc("*/5 * * * *", func() { go CheckCache() })
//timer.AddFunc("* */30 * * *", func() { go c.CheckReddit(ctx) })
//timer.AddFunc("* */1 * * *", func() { go CheckYoutube() })
//timer.AddFunc("* */1 * * *", func() { go CheckFfxiv() })
//timer.AddFunc("* */1 * * *", func() { go CheckTwitch() })
c.timer = timer
return c
}
func (c *Cron) Start() {
c.timer.Start()
}
func (c *Cron) Stop() {
c.timer.Stop()
}
// This is the main entry point to query all the reddit services
func CheckReddit(ctx context.Context) {
sources, err := _queries.ListSourcesBySource(ctx, "reddit")
func (c *Cron) CheckReddit(ctx context.Context) {
sources, err := c.Db.ListSourcesBySource(*c.ctx, "reddit")
if err != nil {
log.Printf("No defines sources for reddit to query - %v\r", err)
}
@ -63,13 +79,13 @@ func CheckReddit(ctx context.Context) {
log.Println(err)
}
redditArticles := rc.ConvertToArticles(raw)
checkPosts(ctx, redditArticles)
c.checkPosts(*c.ctx, redditArticles)
}
}
func CheckYoutube(ctx context.Context) {
func (c *Cron) CheckYoutube(ctx context.Context) {
// Add call to the db to request youtube sources.
sources, err := _queries.ListSourcesBySource(ctx, "youtube")
sources, err := c.Db.ListSourcesBySource(*c.ctx, "youtube")
if err != nil {
log.Printf("Youtube - No sources found to query - %v\r", err)
}
@ -83,12 +99,12 @@ func CheckYoutube(ctx context.Context) {
if err != nil {
log.Println(err)
}
checkPosts(ctx, raw)
c.checkPosts(*c.ctx, raw)
}
}
func CheckFfxiv(ctx context.Context) {
sources, err := _queries.ListSourcesBySource(ctx, "ffxiv")
func (c *Cron) CheckFfxiv(ctx context.Context) {
sources, err := c.Db.ListSourcesBySource(*c.ctx, "ffxiv")
if err != nil {
log.Printf("Final Fantasy XIV - No sources found to query - %v\r", err)
}
@ -102,12 +118,12 @@ func CheckFfxiv(ctx context.Context) {
if err != nil {
log.Println(err)
}
checkPosts(ctx, items)
c.checkPosts(*c.ctx, items)
}
}
func CheckTwitch(ctx context.Context) error {
sources, err := _queries.ListSourcesBySource(ctx, "twitch")
func (c *Cron) CheckTwitch(ctx context.Context) error {
sources, err := c.Db.ListSourcesBySource(*c.ctx, "twitch")
if err != nil {
log.Printf("Twitch - No sources found to query - %v\r", err)
}
@ -126,17 +142,17 @@ func CheckTwitch(ctx context.Context) error {
if err != nil {
log.Println(err)
}
checkPosts(ctx, items)
c.checkPosts(*c.ctx, items)
}
return nil
}
func checkPosts(ctx context.Context, posts []database.Article) {
func (c *Cron) checkPosts(ctx context.Context, posts []database.Article) {
for _, item := range posts {
_, err := _queries.GetArticleByUrl(ctx, item.Url)
_, err := c.Db.GetArticleByUrl(*c.ctx, item.Url)
if err != nil {
err = postArticle(ctx, item)
err = c.postArticle(ctx, item)
if err != nil {
log.Printf("Reddit - Failed to post article - %v - %v.\r", item.Url, err)
} else {
@ -147,8 +163,8 @@ func checkPosts(ctx context.Context, posts []database.Article) {
time.Sleep(30 * time.Second)
}
func postArticle(ctx context.Context, item database.Article) error {
err := _queries.CreateArticle(ctx, database.CreateArticleParams{
func (c *Cron) postArticle(ctx context.Context, item database.Article) error {
err := c.Db.CreateArticle(*c.ctx, database.CreateArticleParams{
ID: uuid.New(),
Sourceid: item.Sourceid,
Tags: item.Tags,

View File

@ -14,23 +14,20 @@ func TestInvokeTwitch(t *testing.T) {
// TODO add database mocks but not sure how to do that yet.
func TestCheckReddit(t *testing.T) {
ctx := context.Background()
cron.OpenDatabase(ctx)
cron.CheckReddit(ctx)
c := cron.Cron{}
c.CheckReddit(ctx)
}
func TestCheckYouTube(t *testing.T) {
ctx := context.Background()
cron.OpenDatabase(ctx)
cron.CheckYoutube(ctx)
c := cron.Cron{}
c.CheckYoutube(ctx)
}
func TestCheckTwitch(t *testing.T) {
ctx := context.Background()
err := cron.OpenDatabase(ctx)
if err != nil {
t.Error(err)
}
err = cron.CheckTwitch(ctx)
c := cron.Cron{}
err := c.CheckTwitch(ctx)
if err != nil {
t.Error(err)
}