Skip to content

drewlakee/chattweiler

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Chattweiler

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 πŸ€–

Features

  • 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

Application context schema

logo

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.

Quickstart

Application deployment preparations

Setting up a community chat

  1. Create a public community in VK
  2. Create a new chat inside the community: Manage > Chats > Create Chat
  3. Once you got to a page of the chat, remember the chat's number (e.g. 9)

Screenshot 2022-10-31 165551

Setting up a Yandex Object Storage

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.

Phrases

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.

Commands

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 with info type sends in chat a phrase with the same type

Membership warnings

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.

Local application deployment

Application configurations

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 like YCN1Ze...SJv)
    • yandex.object.storage.secret.access.key (e.g. some token like YCA...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.g command_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

Deployment

  1. Clone the project git clone git@github.com:drewlakee/chattweiler.git
  2. Build a docker image ./chattweiler/build.sh
  3. Create a configuration file touch bot.env and fill the mandatory variables
  4. Run a container with the image you've just built ./chattweiler/run.sh
  5. Make fun out of it πŸ‘Ύ
Usage examples

Screenshot 2022-10-31 at 16-11-04 Messenger

Screenshot 2022-10-31 at 16-13-06 Messenger

** 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 ...