Chattweiler is a server-side application that handles events which come from VK community chat
Inspirations for the development are:
- Requesting of content from custom sources
- Customized responses
- Fake users filtering
- Forcing to join a community, in case, if you're wanting to be a part of a chat
- Fun π€
- Customized chat responses for chat joins, leavings, warnings, failed commands etc.
- Customized commands for content (e.g. pictures, audio, videos)
- Automatic membership checking and warning
As you might already have noticed the application uses for storage Yandex Object Storage solution, so for using the application you have to have access to such resource. Storage configuration for the application is mentioned further in Quickstart.
In brief, the application operates over csv files that are stored in cloud. That type of file is picked up because it's very straightforward to store and edit. The application caches these files and invalidates over time. That way makes positive effect on performance during events handling.
- Create a public community in VK
- Create a new chat inside the community: Manage > Chats > Create Chat
- Once you got to a page of the chat, remember the chat's number (e.g. 9)
The application uses several buckets:
- commands
- commands_production.csv
- membership-warnings (optional, used automatically by the application if so configured)
- phrases
- phrases_production.csv
For further configurations you have to have such buckets in your environment.
File must contain rows with a specific structure:
type PhraseType string
const (
// for new users in chat
WelcomeType PhraseType = "welcome"
// for users who left
GoodbyeType PhraseType = "goodbye"
// for users who in chat but not in a community
MembershipWarningType PhraseType = "membership_warning"
// for some general info like commands description
InfoType PhraseType = "info"
// for responses with content requests
ContentRequestType PhraseType = "content_request"
// for cases where the application failed to find something
RetryType PhraseType = "retry_request"
)
type Phrase struct {
PhraseID int `csv:"phrase_id"`
// used for probability
// https://en.wikipedia.org/wiki/Fitness_proportionate_selection
// in brief, if its value more than others` value it has more chances to be picked up
Weight int `csv:"weight"`
PhraseType PhraseType `csv:"phrase_type"`
// use null if command is not supposed to use it
VkAudioId string `csv:"vk_audio_id"`
// use null if command is not supposed to use it
VkGifId string `csv:"vk_gif_id"`
// actual text of a phrase
Text string `csv:"text"`
}
csv file:
phrase_id1,weight,phrase_type,vk_audio_id,vk_gif_id,text
phrase_id2,weight,phrase_type,vk_audio_id,vk_gif_id,text
...
1,100,welcome,null,doc120747496_641221964,"Hello there, %username%!"
5,100,membership_warning,null,doc120747496_641228085,"%username%, this chat is only for community members π»\nPlease subscribe quickly!"
18,100,retry_request,null,doc120747496_646353718,"%username%, oops, we've failed, try again ππ»ππ»"
Phrases are used for responses on different types of events.
File must contain rows with a specific structure:
type CommandType string
const (
InfoCommand CommandType = "info"
ContentCommand CommandType = "content"
)
// CsvCommand storage specific object of Command
type CsvCommand struct {
ID int `csv:"id"`
Commands string `csv:"commands"`
Type CommandType `csv:"command_type"`
MediaContentTypes string `csv:"media_types"`
CommunityIDs string `csv:"community_ids"`
}
csv file:
id1,"alias1,alias2",command_type,"media_type1,media_type2","community_id1,community_id2"
id2,"alias1,alias2",command_type,"media_type1,media_type2","community_id1,community_id2"
...
6,"jazzy music,π₯Έ",content,audio,jazzjazz
26,"πΎ,commands",info,,
-
A command could have several aliases which users can call on in chat
-
A command can use several communities to fetch content from it
-
A command can has several media-content types to fetch from communities (randomly chosen per call)
-
command_type
used for different types of command. There's a couple of them right now, command withinfo
type sends in chat a phrase with the same type
type MembershipWarning struct {
WarningID int `csv:"warning_id"`
UserID int `csv:"user_id"`
Username string `csv:"username"`
// when user got first warning in chat about community membership
FirstWarningTs time.Time `csv:"first_warning_ts"`
// a period in which he has to subscribe, or he'll be kicked eventually
GracePeriod string `csv:"grace_period"`
// actual status of a warning
// if a user got a warning and subscribed, then status will be updated
IsRelevant bool `csv:"is_relevant"`
}
If you want to use such feature, then that structure will be used to upload actual status about warnings to a storage bucket by days.
- 2022-23-10
- 2022-24-10
- 2022-25-10
- .....
Such files occur only if warnings happen in a day, so there could be some gaps between files.
Mandatory configurations
vk.community.bot.token
A specific token for your community (e.g. "956c94e96...6039be4e")
How to get: Enter your community > Manage > Settings > API usage > Access tokens
vk.community.id
A specific community id (e.g. "161...464" as a number)
You can get it somewhere in a community or by picking up from some wallpost's url https://vk.com/community?w=wall-<id>_3394
vk.community.chat.id
An actual number of a chat, we've mentioned it earlier in the Quickstart
- Yandex Object Storage
yandex.object.storage.access.key.id
(e.g. some token likeYCN1Ze...SJv
)yandex.object.storage.secret.access.key
(e.g. some token likeYCA...cQ
)yandex.object.storage.region
(e.g.ru-central1
)yandex.object.storage.phrases.bucket
(e.g.phrases-bucket
)yandex.object.storage.phrases.bucket.key
(e.g.phrases_production.csv
)yandex.object.storage.content.command.bucket
(e.g.command-bucket
)yandex.object.storage.content.command.bucket.key
(e.gcommand_production.csv
)yandex.object.storage.membership.warning.bucket
(e.g.membership-warning-bucket
)
Read the documentation how to get these values
Optional configurations
-
vk.admin.user.token
(by default not specified) if you're supposed to use content requesting, you have to have that one. Read the documentation how to get such token -
chat.warden.membership.check.interval
(default:10m
) a periodic interval after which the application goes to VK-API to compare actual members in a chat -
chat.warden.membership.grace.period
(default:1h
) a period after which the application checks if a warned user subscribed to a community -
chat.use.first.name.instead.username
(default:false
) either uses actual name of a user or his url-uid for communication (e.g. "John" or "john_2001") -
content.command.cache.refresh.interval
(default:15m
) a periodic interval after which the application invalidates its cache with commands -
content.requests.queue.size
(default:100
) a buffered channel size between event handler and command executors -
content.garbage.collectors.cleaning.interval
(default:10m
) a periodic interval after which the application removes already unused content collectors which are cached -
phrases.cache.refresh.interval
(default:15m
) a periodic interval after which the application invalidates its cache with phrases -
content.audio.max.cached.attachments
(default:100
) a max number of content that could be stored in an application's cache -
content.audio.cache.refresh.threshold
(default:0.2
) a threshold for a cache with content after which the cache fills out by new content -
content.picture.max.cached.attachments
(default:100
) a max number of content that could be stored in an application's cache -
content.picture.cache.refresh.threshold
(default:0.2
) a threshold for a cache with content after which the cache fills out by new content -
content.video.max.cached.attachments
(default:100
) a max number of content that could be stored in an application's cache -
content.video.cache.refresh.threshold
(default:0.2
) a threshold for a cache with content after which the cache fills out by new content -
bot.functionality.welcome.new.members
(default:true
) enables welcome functionality -
bot.functionality.goodbye.members
(default:true
) enables goodbye functionality -
bot.functionality.membership.checking
(default:false
) enables membership checking functionality -
bot.functionality.content.commands
(default:false
) enables requesting of media content functionality -
bot.log.file
(default:false
) enables writing of a log file near an execution file
- Clone the project
git clone [email protected]:drewlakee/chattweiler.git
- Build a docker image
./chattweiler/build.sh
- Create a configuration file
touch bot.env
and fill the mandatory variables - Run a container with the image you've just built
./chattweiler/run.sh
- Make fun out of it πΎ
** If you are supposed to use file logging, you can make a volume by adding to the command in ./chattweiler/run.sh
a piece of settings docker run -v /path/to/your/log/directory:/application/logs ...