mirror of
https://github.com/InsanusMokrassar/BooruGrabberTelegramBot.git
synced 2026-05-09 17:00:04 +00:00
Compare commits
3 Commits
f33aaa9eb5
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| aaf01d686b | |||
| 348f17127f | |||
| f546c4791f |
93
README.md
93
README.md
@@ -13,6 +13,99 @@ Bot for booru-boards grabbing. It uses [imageboard-api](https://github.com/Kodeh
|
|||||||
|
|
||||||
Sample bot presented here: [@booru_grabber_bot](https://t.me/booru_grabber_bot)
|
Sample bot presented here: [@booru_grabber_bot](https://t.me/booru_grabber_bot)
|
||||||
|
|
||||||
|
## Fast how to start
|
||||||
|
|
||||||
|
1. Create a `config.json`. Minimal required config:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"token": "your bot token",
|
||||||
|
"database": {
|
||||||
|
"url": "jdbc:postgresql://booru_grabber_postgres:5432/test",
|
||||||
|
"username": "test",
|
||||||
|
"password": "test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
All available properties:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"token": "your bot token",
|
||||||
|
"database": {
|
||||||
|
"url": "jdbc:postgresql://booru_grabber_postgres:5432/test",
|
||||||
|
"driver": "org.postgresql.Driver",
|
||||||
|
"username": "test",
|
||||||
|
"password": "test",
|
||||||
|
"reconnectOptions": {
|
||||||
|
"attempts": 3,
|
||||||
|
"delay": 1000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"connectionTimeoutMillis": null,
|
||||||
|
"requestTimeoutMillis": null,
|
||||||
|
"responseTimeoutMillis": null,
|
||||||
|
"proxy": {
|
||||||
|
"hostname": "proxy.example.com",
|
||||||
|
"type": "socks",
|
||||||
|
"port": 1080,
|
||||||
|
"username": null,
|
||||||
|
"password": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Property reference:
|
||||||
|
|
||||||
|
**`token`** *(required)* — Telegram bot token from [@BotFather](https://t.me/BotFather).
|
||||||
|
|
||||||
|
**`database`** *(required)*:
|
||||||
|
|
||||||
|
- `url` — JDBC connection URL (default: `jdbc:pgsql://localhost:12346/test`)
|
||||||
|
- `driver` — JDBC driver class (default: `org.postgresql.Driver`)
|
||||||
|
- `username` — database user (default: empty)
|
||||||
|
- `password` — database password (default: empty)
|
||||||
|
- `reconnectOptions.attempts` — how many times to retry connecting on startup (default: `3`)
|
||||||
|
- `reconnectOptions.delay` — delay in milliseconds between retries (default: `1000`)
|
||||||
|
|
||||||
|
**`client`** *(optional)* — HTTP client settings for Telegram API requests:
|
||||||
|
|
||||||
|
- `connectionTimeoutMillis` — connection timeout in ms
|
||||||
|
- `requestTimeoutMillis` — write/request timeout in ms
|
||||||
|
- `responseTimeoutMillis` — read/response timeout in ms
|
||||||
|
- `proxy.hostname` *(required if proxy set)* — proxy host
|
||||||
|
- `proxy.type` — `socks` (default) or `http`
|
||||||
|
- `proxy.port` — proxy port (default: `1080` for socks, `3128` for http)
|
||||||
|
- `proxy.username` — proxy username (optional; for socks, password is required when username is set)
|
||||||
|
- `proxy.password` — proxy password (optional)
|
||||||
|
|
||||||
|
2. In `docker-compose.yml`, uncomment the `booru_grabber_bot` service and set the path to your config file:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
booru_grabber_postgres:
|
||||||
|
image: postgres
|
||||||
|
container_name: "booru_grabber_postgres"
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: "test"
|
||||||
|
POSTGRES_PASSWORD: "test"
|
||||||
|
POSTGRES_DB: "test"
|
||||||
|
booru_grabber_bot:
|
||||||
|
image: insanusmokrassar/booru_grabber_bot
|
||||||
|
container_name: "booru_grabber_bot"
|
||||||
|
volumes:
|
||||||
|
- "/absolute/path/to/config.json:/booru_grabber_bot/config.json:ro"
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Start the services:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
## Available commands
|
## Available commands
|
||||||
|
|
||||||
Bot have two helping commands: `/start` and `/help`. These commands will return help for bot `/request`/`/enable` commands:
|
Bot have two helping commands: `/start` and `/help`. These commands will return help for bot `/request`/`/enable` commands:
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ org.gradle.parallel=true
|
|||||||
kotlin.js.generate.externals=true
|
kotlin.js.generate.externals=true
|
||||||
kotlin.incremental=true
|
kotlin.incremental=true
|
||||||
|
|
||||||
docker_version=0.0.5
|
docker_version=0.0.6
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
[versions]
|
[versions]
|
||||||
|
|
||||||
kotlin = "2.0.21"
|
kotlin = "2.3.21"
|
||||||
tgbotapi = "20.0.1"
|
tgbotapi = "33.1.0"
|
||||||
microutils = "0.23.0"
|
microutils = "0.29.2"
|
||||||
imageboard = "2.7.0"
|
imageboard = "2.6.1.1"
|
||||||
krontab = "2.6.1"
|
krontab = "2.9.0"
|
||||||
kslog = "1.3.6"
|
kslog = "1.6.1"
|
||||||
ktor = "3.0.1"
|
ktor = "3.4.3"
|
||||||
exposed = "0.55.0"
|
exposed = "1.2.0"
|
||||||
psql = "42.7.4"
|
psql = "42.7.11"
|
||||||
clikt = "5.0.1"
|
clikt = "5.1.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
|
|
||||||
|
|||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.4-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
@@ -106,12 +106,12 @@ suspend fun main(args: Array<String>) {
|
|||||||
}
|
}
|
||||||
result.take(settings.count)
|
result.take(settings.count)
|
||||||
}.takeIf { it.isNotEmpty() } ?: return
|
}.takeIf { it.isNotEmpty() } ?: return
|
||||||
runCatchingSafely {
|
runCatchingLogging {
|
||||||
val urls = result.map { it.url }
|
val urls = result.map { it.url }
|
||||||
chatsUrlsSeenRepo.add(chatId, urls)
|
chatsUrlsSeenRepo.add(chatId, urls)
|
||||||
seenUrls.addAll(urls)
|
seenUrls.addAll(urls)
|
||||||
when {
|
when {
|
||||||
urls.isEmpty() -> return@runCatchingSafely
|
urls.isEmpty() -> return@runCatchingLogging
|
||||||
urls.size == 1 -> sendPhoto(
|
urls.size == 1 -> sendPhoto(
|
||||||
chatId,
|
chatId,
|
||||||
FileUrl(urls.first()),
|
FileUrl(urls.first()),
|
||||||
@@ -143,24 +143,24 @@ suspend fun main(args: Array<String>) {
|
|||||||
chatsChangingMutex.withLock {
|
chatsChangingMutex.withLock {
|
||||||
chatsSendingJobs[chatId] ?.cancel()
|
chatsSendingJobs[chatId] ?.cancel()
|
||||||
settings ?.scheduler ?.let {
|
settings ?.scheduler ?.let {
|
||||||
chatsSendingJobs[chatId] = it.asFlowWithDelays().subscribeSafelyWithoutExceptions(scope) {
|
chatsSendingJobs[chatId] = it.asFlowWithDelays().subscribeLoggingDropExceptions(scope) {
|
||||||
triggerSendForChat(chatId, settings)
|
triggerSendForChat(chatId, settings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repo.onNewValue.subscribeSafelyWithoutExceptions(this) {
|
repo.onNewValue.subscribeLoggingDropExceptions(this) {
|
||||||
refreshChatJob(it.first, it.second)
|
refreshChatJob(it.first, it.second)
|
||||||
}
|
}
|
||||||
repo.onValueRemoved.subscribeSafelyWithoutExceptions(this) {
|
repo.onValueRemoved.subscribeLoggingDropExceptions(this) {
|
||||||
refreshChatJob(it, null)
|
refreshChatJob(it, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
doForAllWithNextPaging {
|
doForAllWithNextPaging {
|
||||||
repo.keys(it).also {
|
repo.keys(it).also {
|
||||||
it.results.forEach {
|
it.results.forEach {
|
||||||
runCatchingSafely {
|
runCatchingLogging {
|
||||||
refreshChatJob(it, null)
|
refreshChatJob(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,16 +173,16 @@ suspend fun main(args: Array<String>) {
|
|||||||
onCommand("enable", requireOnlyCommandInMessage = false) {
|
onCommand("enable", requireOnlyCommandInMessage = false) {
|
||||||
val args = it.content.textSources.drop(1).joinToString("") { it.source }.split(" ")
|
val args = it.content.textSources.drop(1).joinToString("") { it.source }.split(" ")
|
||||||
val parser = EnableArgsParser()
|
val parser = EnableArgsParser()
|
||||||
runCatchingSafely {
|
runCatchingLogging {
|
||||||
parser.parse(args)
|
parser.parse(args)
|
||||||
repo.set(ChatId(it.chat.id.chatId), parser.resultSettings ?: return@runCatchingSafely)
|
repo.set(ChatId(it.chat.id.chatId), parser.resultSettings ?: return@runCatchingLogging)
|
||||||
}.onFailure { e ->
|
}.onFailure { e ->
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
if (it.chat is PrivateChat) {
|
if (it.chat is PrivateChat) {
|
||||||
reply(it, parser.getFormattedHelp()!!)
|
reply(it, parser.getFormattedHelp()!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
runCatchingSafely {
|
runCatchingLogging {
|
||||||
if (it.chat is ChannelChat) {
|
if (it.chat is ChannelChat) {
|
||||||
delete(it)
|
delete(it)
|
||||||
}
|
}
|
||||||
@@ -200,7 +200,7 @@ suspend fun main(args: Array<String>) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val parser = EnableArgsParser(repo.get(ChatId(it.chat.id.chatId)) ?: ChatSettings.DEFAULT)
|
val parser = EnableArgsParser(repo.get(ChatId(it.chat.id.chatId)) ?: ChatSettings.DEFAULT)
|
||||||
runCatchingSafely {
|
runCatchingLogging {
|
||||||
parser.parse(args)
|
parser.parse(args)
|
||||||
parser.resultSettings
|
parser.resultSettings
|
||||||
}.onFailure { e ->
|
}.onFailure { e ->
|
||||||
@@ -214,18 +214,18 @@ suspend fun main(args: Array<String>) {
|
|||||||
triggerSendForChat(ChatId(it.chat.id.chatId), chatSettings ?: return@onCommand)
|
triggerSendForChat(ChatId(it.chat.id.chatId), chatSettings ?: return@onCommand)
|
||||||
}
|
}
|
||||||
onCommand("disable", requireOnlyCommandInMessage = true) {
|
onCommand("disable", requireOnlyCommandInMessage = true) {
|
||||||
runCatchingSafely {
|
runCatchingLogging {
|
||||||
repo.unset(ChatId(it.chat.id.chatId))
|
repo.unset(ChatId(it.chat.id.chatId))
|
||||||
}
|
}
|
||||||
runCatchingSafely {
|
runCatchingLogging {
|
||||||
delete(it)
|
delete(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onCommand("take_settings", requireOnlyCommandInMessage = true) {
|
onCommand("take_settings", requireOnlyCommandInMessage = true) {
|
||||||
val settings = runCatchingSafely {
|
val settings = runCatchingLogging {
|
||||||
repo.get(ChatId(it.chat.id.chatId))
|
repo.get(ChatId(it.chat.id.chatId))
|
||||||
}.getOrNull()
|
}.getOrNull()
|
||||||
runCatchingSafely {
|
runCatchingLogging {
|
||||||
if (settings == null) {
|
if (settings == null) {
|
||||||
reply(it, "You didn't enable requesting")
|
reply(it, "You didn't enable requesting")
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@file:OptIn(ExperimentalSerializationApi::class)
|
||||||
|
|
||||||
import dev.inmo.krontab.*
|
import dev.inmo.krontab.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -44,6 +46,8 @@ data class ChatSettings(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExperimentalSerializationApi
|
||||||
|
@Suppress("EXTERNAL_SERIALIZER_USELESS")
|
||||||
@Serializer(DefaultBoards::class)
|
@Serializer(DefaultBoards::class)
|
||||||
object BoardSerializer : KSerializer<DefaultBoards> {
|
object BoardSerializer : KSerializer<DefaultBoards> {
|
||||||
override val descriptor: SerialDescriptor = String.serializer().descriptor
|
override val descriptor: SerialDescriptor = String.serializer().descriptor
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import dev.inmo.kslog.common.e
|
|||||||
import dev.inmo.kslog.common.logger
|
import dev.inmo.kslog.common.logger
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.Transient
|
import kotlinx.serialization.Transient
|
||||||
import org.jetbrains.exposed.sql.Database
|
import org.jetbrains.exposed.v1.jdbc.Database
|
||||||
import org.jetbrains.exposed.sql.transactions.transactionManager
|
import org.jetbrains.exposed.v1.jdbc.transactions.transactionManager
|
||||||
import org.postgresql.Driver
|
import org.postgresql.Driver
|
||||||
import java.sql.Connection
|
import java.sql.Connection
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user