Исправлена ошибка, при которой после таймаута функция NewsText ничего не возвращала и программа падала. Так же теперь лента читается начиная от самого старого поста, заканчивая самым новым.
This commit is contained in:
parent
47242e5431
commit
a79933b724
8 changed files with 70 additions and 29 deletions
45
README.md
45
README.md
|
|
@ -10,33 +10,23 @@
|
|||
|
||||
* golang 1.24.2+
|
||||
* redis
|
||||
* gnu make
|
||||
|
||||
Далее надо пройтись по всем папкам и сделать ```go mod tidy```:
|
||||
|
||||
После необходимо произвести сборку утилиты с помощью команды ```make```:
|
||||
```
|
||||
cd config
|
||||
go mod tidy
|
||||
cd ../stacker
|
||||
go mod tidy
|
||||
cd ../tooter
|
||||
go mod tidy
|
||||
cd ../service
|
||||
go mod tidy
|
||||
cd ../
|
||||
```
|
||||
|
||||
После уже собрать саму программу:
|
||||
```
|
||||
go build -C service -o ../kiki
|
||||
make
|
||||
```
|
||||
|
||||
В папке с проектом появится исполняемый файл kiki. Перед запуском необходимо заполнить конфигурационный файл config.yaml (пример заполнения представлен в файле config.example.yaml)
|
||||
```
|
||||
instance: https://pleroma.catgirls.asia #Инстанс, на котором находится аккаунт, в который будет происходить постинг
|
||||
rss_url: https://4pda.to/feed #Новостная лента
|
||||
sensitive: true #Ставить ли плашку NSFW
|
||||
instance: https://pleroma.catgirls.asia #Адрес инстанса
|
||||
rss_urls: #YAML массив лент
|
||||
- url: https://habr.com/ru/rss/flows/admin/articles/?fl=ru #Адрес ленты
|
||||
sensitive: false #Нужно ли ставить NSFW плашку
|
||||
- url: https://4pda.to/feed
|
||||
sensitive: false
|
||||
redis:
|
||||
address: localhost:6379 #Адрес Redis
|
||||
address: localhost:6379 #Адрес Redis, в случае использования Docker Compose необходимо написать redis:6379
|
||||
```
|
||||
|
||||
После чего необходимо провести инициализацию для получения секретов аккаунта. **СЕКРЕТЫ ХРАНЯТСЯ В ОТКРЫТОМ ВИДЕ, ТАК ЧТО БУДЬТЕ ОСТОРОЖНЫ!**
|
||||
|
|
@ -47,6 +37,11 @@ redis:
|
|||
|
||||
После этого программу можно запустить командой ```./kiki run```. Перед запуском убедитесь что Redis запущен.
|
||||
|
||||
Если вам надо удалить утилиту и почистить все go.sum файлы, то можно выполнить следующую команду:
|
||||
```
|
||||
make clean
|
||||
```
|
||||
|
||||
# Запуск в Docker
|
||||
|
||||
Для запуска программы в Docker необходимо создать два файла: secret.conf и config.yaml:
|
||||
|
|
@ -78,9 +73,13 @@ docker compose up -d
|
|||
* [x] Добавление картинок в пост
|
||||
* [x] Добавление поддержки Redis
|
||||
* [x] Упаковка в Docker образ
|
||||
* [ ] Создание Makefile для более удобной сборки
|
||||
* [ ] Поддержка обновления данных из конфига "на лету"
|
||||
* [ ] Добавление поддержки нескольких лент
|
||||
* [x] Создание Makefile для более удобной сборки
|
||||
* [x] Поддержка обновления данных из конфига "на лету"
|
||||
* [x] Добавление поддержки нескольких лент
|
||||
* [ ] Добавить поддержку шаблонов
|
||||
* [ ] Добавить сбор информации о инстансе, кол-ве поддерживаемых символов и кол-ве поддерживаемых медиа в одном посте. Сделать обработку этой информации и формирование постов с учетом инстансоспецифичных факторов
|
||||
* [ ] Некоторые RSS-ленты содержат видео. Добавить поддержку видео в постах
|
||||
* [ ] Дополнительные флаги для команды чтобы указывать где лежит конфиг
|
||||
|
||||
# Отказ от ответственности
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
instance: https://pleroma.catgirls.asia
|
||||
rss_url: https://4pda.to/feed
|
||||
sensitive: true
|
||||
rss_urls:
|
||||
- url: https://habr.com/ru/rss/flows/admin/articles/?fl=ru
|
||||
sensitive: false
|
||||
- url: https://4pda.to/feed
|
||||
sensitive: false
|
||||
redis:
|
||||
address: localhost:6379
|
||||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// Сруктура config.yaml
|
||||
type KikiSettings struct {
|
||||
Instance string `yaml:"instance,omitempty"`
|
||||
RSSURLs []struct {
|
||||
|
|
@ -19,6 +20,7 @@ type KikiSettings struct {
|
|||
} `yaml:"redis"`
|
||||
}
|
||||
|
||||
// Структура secret.conf
|
||||
type MastodonClientData struct {
|
||||
ClientID string `yaml:"clientID,omitempty"`
|
||||
ClientSecret string `yaml:"clientSecret,omitempty"`
|
||||
|
|
@ -26,6 +28,7 @@ type MastodonClientData struct {
|
|||
Instance string `yaml:"instance,omitempty"`
|
||||
}
|
||||
|
||||
// Получение данных из конфига config.yaml
|
||||
func GetKikiConfig(path string) KikiSettings {
|
||||
var kikiSettings KikiSettings
|
||||
|
||||
|
|
@ -42,6 +45,7 @@ func GetKikiConfig(path string) KikiSettings {
|
|||
return kikiSettings
|
||||
}
|
||||
|
||||
// Получение данных из конфига secret.conf
|
||||
func GetSecrets(path string) *mastodon.Config {
|
||||
var clientData MastodonClientData
|
||||
|
||||
|
|
|
|||
|
|
@ -10,4 +10,6 @@ services:
|
|||
redis:
|
||||
image: redis
|
||||
restart: always
|
||||
volumes:
|
||||
- ./redis_data:/data
|
||||
|
||||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// Проверка на изменение в файле filename
|
||||
func IsFileChange(lastMod *time.Time, filename string) bool {
|
||||
fileStat, err := os.Stat(filename)
|
||||
|
||||
|
|
@ -20,6 +21,7 @@ func IsFileChange(lastMod *time.Time, filename string) bool {
|
|||
}
|
||||
}
|
||||
|
||||
// Получение информации о том, когда файл изменился
|
||||
func GetFileModTime(filename string) (time.Time, error) {
|
||||
fileStat, err := os.Stat(filename)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"kiki/config"
|
||||
|
|
@ -24,7 +25,7 @@ func main() {
|
|||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "init",
|
||||
Usage: "Инициализировать клиента",
|
||||
Usage: "Инициализировать клиента и создать secret.conf, в котором будут храниться данные для доступа к учетной записи",
|
||||
Action: func(ctx context.Context, cmd *cli.Command) error {
|
||||
confFile, err := filepath.Abs("config.yaml")
|
||||
kikiConfig := config.GetKikiConfig(confFile)
|
||||
|
|
@ -80,7 +81,7 @@ func main() {
|
|||
for _, rssUrl := range kikiConfig.RSSURLs {
|
||||
newPost := tooter.NewsText(rssUrl.Url)
|
||||
|
||||
for _, post := range newPost {
|
||||
for _, post := range slices.Backward(newPost) {
|
||||
inStack, err := stacker.CheckInRedis(rdb, post.GUID)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
// Создание подключения к Redis
|
||||
func ConnectToRedis(addr string) redis.Client {
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: addr,
|
||||
|
|
@ -16,6 +17,7 @@ func ConnectToRedis(addr string) redis.Client {
|
|||
return *rdb
|
||||
}
|
||||
|
||||
// Создание записи в Redis о том, что этот пост отправлен
|
||||
func SetToRedis(rdb redis.Client, key string, val interface{}) error {
|
||||
err := rdb.Set(context.Background(), key, val, 0).Err()
|
||||
if err != nil {
|
||||
|
|
@ -24,6 +26,7 @@ func SetToRedis(rdb redis.Client, key string, val interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Проверка есть ли пост в Redis или нет
|
||||
func CheckInRedis(rdb redis.Client, key string) (bool, error) {
|
||||
_, err := rdb.Get(context.Background(), key).Result()
|
||||
if err == redis.Nil {
|
||||
|
|
@ -34,6 +37,7 @@ func CheckInRedis(rdb redis.Client, key string) (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
// Сохранение базы данных Redis
|
||||
func SaveRedis(rdb redis.Client) error {
|
||||
err := rdb.Save(context.Background()).Err()
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
|
@ -18,6 +19,7 @@ import (
|
|||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// Функция создает файл secret.conf, в котором хранятся данные для доступа к аккаунту
|
||||
func ClientConfiguration(Instance string) {
|
||||
appConfig := &mastodon.AppConfig{
|
||||
Server: Instance,
|
||||
|
|
@ -37,7 +39,7 @@ func ClientConfiguration(Instance string) {
|
|||
log.Println(err)
|
||||
}
|
||||
var userToken string
|
||||
//fmt.Println(u)
|
||||
|
||||
fmt.Printf("Перейдите по ссылке\n%s\nИ введите user token ниже:\n", u)
|
||||
fmt.Scanln(&userToken)
|
||||
|
||||
|
|
@ -67,24 +69,38 @@ func ClientConfiguration(Instance string) {
|
|||
}
|
||||
|
||||
log.Println(string(marshaledYaml))
|
||||
|
||||
secretConfig, err := os.OpenFile("secret.conf", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
secretConfig.Write(marshaledYaml)
|
||||
|
||||
defer secretConfig.Close()
|
||||
}
|
||||
|
||||
// Возвращает срез новостей, полученных из RSS ленты
|
||||
func NewsText(url string) []*gofeed.Item {
|
||||
fp := gofeed.NewParser()
|
||||
feed, err := fp.ParseURL(url)
|
||||
|
||||
if err != nil {
|
||||
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
||||
log.Println("Timeout Error:", err)
|
||||
return []*gofeed.Item{}
|
||||
} else {
|
||||
log.Println(err)
|
||||
return []*gofeed.Item{}
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("RSS лента получена")
|
||||
|
||||
return feed.Items
|
||||
}
|
||||
|
||||
// Отправляет созданный пост
|
||||
func CreatePost(mastoClient mastodon.Client, toot mastodon.Toot) {
|
||||
_, err := mastoClient.PostStatus(context.Background(), &toot)
|
||||
if err != nil {
|
||||
|
|
@ -92,25 +108,34 @@ func CreatePost(mastoClient mastodon.Client, toot mastodon.Toot) {
|
|||
}
|
||||
}
|
||||
|
||||
// Обходит срез ссылок на изображения и возвращает срез, состоящий из представления картинок в виде срезов байтов
|
||||
func PicBytesArray(picturesArray []string) [][]byte {
|
||||
var picturesBytes [][]byte
|
||||
|
||||
for _, picture := range picturesArray {
|
||||
resp, err := http.Get(picture)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return picturesBytes
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
picBytes, err := io.ReadAll(resp.Body)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return picturesBytes
|
||||
}
|
||||
|
||||
picturesBytes = append(picturesBytes, picBytes)
|
||||
}
|
||||
|
||||
return picturesBytes
|
||||
}
|
||||
|
||||
// Загружает на инстанс изображения, после чего возвращает массив прикрепленных медиа в Mastodon. Необходимо для получения MediaID для каждого изображения
|
||||
func UploadPictures(mastoClient mastodon.Client, filesBytes [][]byte) []*mastodon.Attachment {
|
||||
var attachments []*mastodon.Attachment
|
||||
|
||||
|
|
@ -128,6 +153,7 @@ func UploadPictures(mastoClient mastodon.Client, filesBytes [][]byte) []*mastodo
|
|||
return attachments
|
||||
}
|
||||
|
||||
// Формирование тела статуса
|
||||
func CreateToot(mastoClient mastodon.Client, newsDesc *gofeed.Item, sensitive bool) (mastodon.Toot, error) {
|
||||
var imgArray []string
|
||||
var attachments []*mastodon.Attachment
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue