ื›ืชื™ื‘ืช ื‘ื•ื˜ ื˜ืœื’ืจื ื‘-R (ื—ืœืง 4): ื‘ื ื™ื™ืช ื“ื™ืืœื•ื’ ืขืงื‘ื™ ื•ื”ื’ื™ื•ื ื™ ืขื ื”ื‘ื•ื˜

ืื ื›ื‘ืจ ืงืจืืช ืืช ื”ืงื•ื“ื ืฉืœื•ืฉื” ืžืืžืจื™ื ืžื”ืกื“ืจื” ื”ื–ื•, ืื– ืืชื” ื›ื‘ืจ ื™ื•ื“ืข ืื™ืš ืœื›ืชื•ื‘ ื‘ื•ื˜ื™ื ืฉืœ ื˜ืœื’ืจื ืžืŸ ื”ืžื ื™ื™ืŸ ืขื ืžืงืœื“ืช.

ื‘ืžืืžืจ ื–ื” ื ืœืžื“ ื›ื™ืฆื“ ืœื›ืชื•ื‘ ื‘ื•ื˜ ืฉื™ืฉืžื•ืจ ืขืœ ื“ื™ืืœื•ื’ ืขืงื‘ื™. ื”ึธื”ึตืŸ. ื”ื‘ื•ื˜ ื™ืฉืืœ ืื•ืชืš ืฉืืœื•ืช ื•ื™ื—ื›ื” ืฉืชื–ื™ืŸ ืงืฆืช ืžื™ื“ืข. ื‘ื”ืชืื ืœื ืชื•ื ื™ื ืฉืชื–ื™ืŸ, ื”ื‘ื•ื˜ ื™ื‘ืฆืข ื›ืžื” ืคืขื•ืœื•ืช.

ื’ื ื‘ืžืืžืจ ื–ื” ื ืœืžื“ ื›ื™ืฆื“ ืœื”ืฉืชืžืฉ ื‘ืžืกื“ ื ืชื•ื ื™ื ืžืชื—ืช ืœืžื›ืกื” ื”ืžื ื•ืข ืฉืœ ื”ื‘ื•ื˜, ื‘ื“ื•ื’ืžื” ืฉืœื ื• ื–ื” ื™ื”ื™ื” SQLite, ืื‘ืœ ืืชื” ื™ื›ื•ืœ ืœื”ืฉืชืžืฉ ื‘ื›ืœ DBMS ืื—ืจ. ื›ืชื‘ืชื™ ื‘ืคื™ืจื•ื˜ ืจื‘ ื™ื•ืชืจ ืขืœ ืื™ื ื˜ืจืืงืฆื™ื” ืขื ืžืกื“ื™ ื ืชื•ื ื™ื ื‘ืฉืคืช R ื‘ ืžืืžืจ ื–ื”.

ื›ืชื™ื‘ืช ื‘ื•ื˜ ื˜ืœื’ืจื ื‘-R (ื—ืœืง 4): ื‘ื ื™ื™ืช ื“ื™ืืœื•ื’ ืขืงื‘ื™ ื•ื”ื’ื™ื•ื ื™ ืขื ื”ื‘ื•ื˜

ื›ืœ ื”ื›ืชื‘ื•ืช ืžื”ืกื“ืจื” "ื›ืชื™ื‘ืช ื‘ื•ื˜ ื˜ืœื’ืจื ื‘-R"

  1. ืื ื• ื™ื•ืฆืจื™ื ื‘ื•ื˜ ื•ืžืฉืชืžืฉื™ื ื‘ื• ื›ื“ื™ ืœืฉืœื•ื— ื”ื•ื“ืขื•ืช ื‘ื˜ืœื’ืจื
  2. ื”ื•ืกืฃ ืชืžื™ื›ื” ื‘ืคืงื•ื“ื•ืช ื•ืžืกื ื ื™ ื”ื•ื“ืขื•ืช ืœื‘ื•ื˜
  3. ื›ื™ืฆื“ ืœื”ื•ืกื™ืฃ ืชืžื™ื›ื” ื‘ืžืงืœื“ืช ืœื‘ื•ื˜
  4. ื‘ื ื™ื™ืช ื“ื™ืืœื•ื’ ืขืงื‘ื™ ื•ื”ื’ื™ื•ื ื™ ืขื ื”ื‘ื•ื˜

ืชื•ื›ืŸ

ืื ืืชื” ืžืขื•ื ื™ื™ืŸ ื‘ื ื™ืชื•ื— ื ืชื•ื ื™ื, ืื•ืœื™ ืืชื” ืžืขื•ื ื™ื™ืŸ ื‘- ืฉืœื™ ืžื‘ืจืง ะธ YouTube ืขืจื•ืฆื™ื. ืจื•ื‘ ื”ืชื•ื›ืŸ ืžื•ืงื“ืฉ ืœืฉืคืช R.

  1. ืžื‘ื•ื
  2. ืชื”ืœื™ืš ื‘ื ื™ื™ืช ื‘ื•ื˜
  3. ืžื‘ื ื” ืคืจื•ื™ืงื˜ ื‘ื•ื˜
  4. ืชืฆื•ืจืช ื‘ื•ื˜
  5. ืฆื•ืจ ืžืฉืชื ื” ืกื‘ื™ื‘ื”
  6. ื™ืฆื™ืจืช ืžืกื“ ื ืชื•ื ื™ื
  7. ื›ืชื™ื‘ืช ืคื•ื ืงืฆื™ื•ืช ืœืขื‘ื•ื“ื” ืขื ืžืกื“ ื”ื ืชื•ื ื™ื
  8. ืฉื™ื˜ื•ืช ื‘ื•ื˜
  9. ืžืกื ื ื™ ื”ื•ื“ืขื•ืช
  10. ืžื˜ืคืœื™ื
  11. ืงื•ื“ ื”ืฉืงืช ื‘ื•ื˜
  12. ืžืกืงื ื”

ืžื‘ื•ื

ืขืœ ืžื ืช ืฉื”ื‘ื•ื˜ ื™ื‘ืงืฉ ืžืžืš ื ืชื•ื ื™ื ื•ื™ืžืชื™ืŸ ืฉืชื–ื™ืŸ ืžื™ื“ืข ื›ืœืฉื”ื•, โ€‹โ€‹ืชืฆื˜ืจืš ืœืชืขื“ ืืช ื”ืžืฆื‘ ื”ื ื•ื›ื—ื™ ืฉืœ ื”ื“ื™ืืœื•ื’. ื”ื“ืจืš ื”ื˜ื•ื‘ื” ื‘ื™ื•ืชืจ ืœืขืฉื•ืช ื–ืืช ื”ื™ื ืœื”ืฉืชืžืฉ ื‘ืกื•ื’ ืฉืœ ืžืกื“ ื ืชื•ื ื™ื ืžืฉื•ื‘ืฅ, ื›ื’ื•ืŸ SQLite.

ื”ึธื”ึตืŸ. ื”ื”ื™ื’ื™ื•ืŸ ื™ื”ื™ื” ื›ื“ืœืงืžืŸ. ืื ื• ืงื•ืจืื™ื ืœืฉื™ื˜ืช ื”ื‘ื•ื˜, ื•ื”ื‘ื•ื˜ ืžื‘ืงืฉ ืžืื™ืชื ื• ื‘ืจืฆืฃ ืžื™ื“ืข ื›ืœืฉื”ื•, โ€‹โ€‹ื•ื‘ื›ืœ ืฉืœื‘ ื”ื•ื ืžืžืชื™ืŸ ืœื”ื–ื ืช ื”ืžื™ื“ืข ื”ื–ื” ื•ื™ื›ื•ืœ ืœื‘ื“ื•ืง ืื•ืชื•.

ื ื›ืชื•ื‘ ืืช ื”ื‘ื•ื˜ ื”ืคืฉื•ื˜ ื‘ื™ื•ืชืจ ื”ืืคืฉืจื™, ืงื•ื“ื ื”ื•ื ื™ื‘ืงืฉ ืืช ืฉืžืš, ืื—ืจ ื›ืš ืืช ื’ื™ืœืš, ื•ื™ืฉืžื•ืจ ืืช ื”ื ืชื•ื ื™ื ืฉื”ืชืงื‘ืœื• ื‘ืžืกื“ ื”ื ืชื•ื ื™ื. ื›ืืฉืจ ืžื‘ืงืฉื™ื ื’ื™ืœ, ื”ื•ื ื™ื‘ื“ื•ืง ืฉื”ื ืชื•ื ื™ื ืฉื”ื•ื–ื ื• ื”ื ืžืกืคืจ ื•ืœื ื˜ืงืกื˜.

ืœื“ื™ืืœื•ื’ ืคืฉื•ื˜ ื›ื–ื” ื™ื”ื™ื• ืจืง ืฉืœื•ืฉื” ืžืฆื‘ื™ื:

  1. start ื”ื•ื ื”ืžืฆื‘ ื”ืจื’ื™ืœ ืฉืœ ื”ื‘ื•ื˜, ืฉื‘ื• ื”ื•ื ืœื ืžืฆืคื” ืœืฉื•ื ืžื™ื“ืข ืžืžืš
  2. wait_name - ืžืฆื‘ ืฉื‘ื• ื”ื‘ื•ื˜ ืžื—ื›ื” ืœื”ื–ื ืช ืฉื
  3. wait_age ื”ื•ื ื”ืžืฆื‘ ืฉื‘ื• ื”ื‘ื•ื˜ ืžื—ื›ื” ืœื”ื–ื ืช ื’ื™ืœืš, ืžืกืคืจ ื”ืฉื ื™ื ื”ืžืœืื•ืช.

ืชื”ืœื™ืš ื‘ื ื™ื™ืช ื‘ื•ื˜

ื‘ืžื”ืœืš ื”ืžืืžืจ, ื ื‘ื ื” ื‘ื•ื˜ ืฆืขื“ ืื—ืจ ืฆืขื“; ื”ืชื”ืœื™ืš ื›ื•ืœื• ื™ื›ื•ืœ ืœื”ื™ื•ืช ืžืชื•ืืจ ื‘ืื•ืคืŸ ืกื›ืžื˜ื™ ื‘ืื•ืคืŸ ื”ื‘ื:
ื›ืชื™ื‘ืช ื‘ื•ื˜ ื˜ืœื’ืจื ื‘-R (ื—ืœืง 4): ื‘ื ื™ื™ืช ื“ื™ืืœื•ื’ ืขืงื‘ื™ ื•ื”ื’ื™ื•ื ื™ ืขื ื”ื‘ื•ื˜

  1. ืื ื• ื™ื•ืฆืจื™ื ืชืฆื•ืจืช ื‘ื•ื˜ ืฉื‘ื” ื ืื—ืกืŸ ื›ืžื” ื”ื’ื“ืจื•ืช. ื‘ืžืงืจื” ืฉืœื ื•, ืืกื™ืžื•ืŸ ื”ื‘ื•ื˜, ื•ื”ื ืชื™ื‘ ืœืงื•ื‘ืฅ ืžืกื“ ื”ื ืชื•ื ื™ื.
  2. ืื ื• ื™ื•ืฆืจื™ื ืžืฉืชื ื” ืกื‘ื™ื‘ื” ื‘ื• ื™ืื•ื—ืกืŸ ื”ื ืชื™ื‘ ืœืคืจื•ื™ืงื˜ ืขื ื”ื‘ื•ื˜.
  3. ืื ื• ื™ื•ืฆืจื™ื ืืช ื‘ืกื™ืก ื”ื ืชื•ื ื™ื ืขืฆืžื•, ื•ืžืกืคืจ ืคื•ื ืงืฆื™ื•ืช ื›ื“ื™ ืฉื”ื‘ื•ื˜ ื™ื•ื›ืœ ืœื™ืฆื•ืจ ืื™ืชื• ืื™ื ื˜ืจืืงืฆื™ื”.
  4. ืื ื—ื ื• ื›ื•ืชื‘ื™ื ืฉื™ื˜ื•ืช ื‘ื•ื˜ื™ื, ื›ืœื•ืžืจ. ื”ืคื•ื ืงืฆื™ื•ืช ืฉื”ื•ื ื™ื‘ืฆืข.
  5. ื”ื•ืกืคืช ืžืกื ื ื™ ื”ื•ื“ืขื•ืช. ื‘ืขื–ืจืชื• ื”ื‘ื•ื˜ ื™ื™ื’ืฉ ืœืฉื™ื˜ื•ืช ื”ื“ืจื•ืฉื•ืช, ื‘ื”ืชืื ืœืžืฆื‘ ื”ื ื•ื›ื—ื™ ืฉืœ ื”ืฆ'ืื˜.
  6. ืื ื• ืžื•ืกื™ืคื™ื ืžื˜ืคืœื™ื ืฉื™ื—ื‘ืจื• ืคืงื•ื“ื•ืช ื•ื”ื•ื“ืขื•ืช ืขื ืฉื™ื˜ื•ืช ื”ื‘ื•ื˜ ื”ื ื“ืจืฉื•ืช.
  7. ื‘ื•ืื• ื ืคืขื™ืœ ืืช ื”ื‘ื•ื˜.

ืžื‘ื ื” ืคืจื•ื™ืงื˜ ื‘ื•ื˜

ืžื˜ืขืžื™ ื ื•ื—ื•ืช, ื ื—ืœืง ืืช ื”ืงื•ื“ ืฉืœ ื”ื‘ื•ื˜ ืฉืœื ื•, ื•ืงื‘ืฆื™ื ืงืฉื•ืจื™ื ืื—ืจื™ื, ืœืžื‘ื ื” ื”ื‘ื.

  • bot.R - ื”ืงื•ื“ ื”ืจืืฉื™ ืฉืœ ื”ื‘ื•ื˜ ืฉืœื ื•
  • db_bot_function.R - ื’ื•ืฉ ืงื•ื“ ืขื ืคื•ื ืงืฆื™ื•ืช ืœืขื‘ื•ื“ื” ืขื ืžืกื“ ื ืชื•ื ื™ื
  • bot_methods.R - ืงื•ื“ ืฉืœ ืฉื™ื˜ื•ืช ื‘ื•ื˜ื™ื
  • message_filters.R - ืžืกื ื ื™ ื”ื•ื“ืขื•ืช
  • ืžื˜ืคืœื™ื.ืจ - ืžื˜ืคืœื™ื
  • config.cfg - ืชืฆื•ืจืช ื‘ื•ื˜
  • create_db_data.sql - ืกืงืจื™ืคื˜ SQL ืœื™ืฆื™ืจืช ื˜ื‘ืœื” ืขื ื ืชื•ื ื™ ืฆ'ืื˜ ื‘ืžืกื“ ื”ื ืชื•ื ื™ื
  • create_db_state.sql - ืกืงืจื™ืคื˜ SQL ืœื™ืฆื™ืจืช ื˜ื‘ืœื” ืฉืœ ืžืฆื‘ ื”ืฆ'ืื˜ ื”ื ื•ื›ื—ื™ ื‘ืžืกื“ ื”ื ืชื•ื ื™ื
  • bot.db - ืžืกื“ ื ืชื•ื ื™ื ืฉืœ ื‘ื•ื˜ื™ื

ืืชื” ื™ื›ื•ืœ ืœื”ืฆื™ื’ ืืช ื›ืœ ืคืจื•ื™ืงื˜ ื”ื‘ื•ื˜, ืื• ืœื”ื•ืจื™ื“ ืžืžื ื™ ืžืื’ืจ ื‘-GitHub.

ืชืฆื•ืจืช ื‘ื•ื˜

ื ืฉืชืžืฉ ื‘ืชืฆื•ืจื” ื”ืจื’ื™ืœื” ืงื•ื‘ืฅ ini, ื”ื˜ื•ืคืก ื”ื‘ื:

[bot_settings]
bot_token=ะขะžะšะ•ะ_ะ’ะะจะ•ะ“ะž_ะ‘ะžะขะ

[db_settings]
db_path=C:/ะŸะฃะขะฌ/ะš/ะŸะะŸะšะ•/ะŸะ ะžะ•ะšะขะ/bot.db

ื‘ืชืฆื•ืจื” ืื ื• ื›ื•ืชื‘ื™ื ืืช ืืกื™ืžื•ืŸ ื”ื‘ื•ื˜ ื•ืืช ื”ื ืชื™ื‘ ืœืžืกื“ ื”ื ืชื•ื ื™ื, ื›ืœื•ืžืจ. ืœืงื•ื‘ืฅ bot.db; ืื ื• ื ื™ืฆื•ืจ ืืช ื”ืงื•ื‘ืฅ ืขืฆืžื• ื‘ืฉืœื‘ ื”ื‘ื.

ืขื‘ื•ืจ ื‘ื•ื˜ื™ื ืžื•ืจื›ื‘ื™ื ื™ื•ืชืจ, ืืชื” ื™ื›ื•ืœ ืœื™ืฆื•ืจ ืชืฆื•ืจื•ืช ืžื•ืจื›ื‘ื•ืช ื™ื•ืชืจ, ื—ื•ืฅ ืžื–ื”, ืื™ืŸ ืฆื•ืจืš ืœื›ืชื•ื‘ ืชืฆื•ืจืช ini, ืืชื” ื™ื›ื•ืœ ืœื”ืฉืชืžืฉ ื‘ื›ืœ ืคื•ืจืžื˜ ืื—ืจ ื›ื•ืœืœ JSON.

ืฆื•ืจ ืžืฉืชื ื” ืกื‘ื™ื‘ื”

ื‘ื›ืœ PC ื ื™ืชืŸ ืœืืชืจ ืืช ื”ืชื™ืงื™ื” ืขื ืคืจื•ื™ืงื˜ ื”ื‘ื•ื˜ ื‘ืกืคืจื™ื•ืช ืฉื•ื ื•ืช ื•ื‘ื›ื•ื ื ื™ื ืฉื•ื ื™ื, ื›ืš ืฉื‘ืงื•ื“ ื”ื ืชื™ื‘ ืœืชื™ืงื™ื™ืช ื”ืคืจื•ื™ืงื˜ ื™ื•ื’ื“ืจ ื‘ืืžืฆืขื•ืช ืžืฉืชื ื” ืกื‘ื™ื‘ื” TG_BOT_PATH.

ื™ืฉื ืŸ ืžืกืคืจ ื“ืจื›ื™ื ืœื™ืฆื•ืจ ืžืฉืชื ื” ืกื‘ื™ื‘ื”, ื”ืคืฉื•ื˜ื” ื‘ื™ื•ืชืจ ื”ื™ื ืœื›ืชื•ื‘ ืื•ืชื• ื‘ืงื•ื‘ืฅ .Renviron.

ืืชื” ื™ื›ื•ืœ ืœื™ืฆื•ืจ ืื• ืœืขืจื•ืš ืงื•ื‘ืฅ ื–ื” ื‘ืืžืฆืขื•ืช ื”ืคืงื•ื“ื” file.edit(path.expand(file.path("~", ".Renviron"))). ื‘ืฆืข ืื•ืชื• ื•ื”ื•ืกืฃ ืฉื•ืจื” ืื—ืช ืœืงื•ื‘ืฅ:

TG_BOT_PATH=C:/ะŸะฃะขะฌ/ะš/ะ’ะะจะ•ะœะฃ/ะŸะ ะžะ•ะšะขะฃ

ืœืื—ืจ ืžื›ืŸ ืฉืžื•ืจ ืืช ื”ืงื•ื‘ืฅ .Renviron ื•ื”ืคืขืœ ืžื—ื“ืฉ ืืช RStudio.

ื™ืฆื™ืจืช ืžืกื“ ื ืชื•ื ื™ื

ื”ืฉืœื‘ ื”ื‘ื ื”ื•ื ื™ืฆื™ืจืช ืžืกื“ ื ืชื•ื ื™ื. ื ืฆื˜ืจืš 2 ืฉื•ืœื—ื ื•ืช:

  • chat_data - ื ืชื•ื ื™ื ืฉื”ื‘ื•ื˜ ื‘ื™ืงืฉ ืžื”ืžืฉืชืžืฉ
  • chat_state - ื”ืžืฆื‘ ื”ื ื•ื›ื—ื™ ืฉืœ ื›ืœ ื”ืฆ'ืื˜ื™ื

ืืชื” ื™ื›ื•ืœ ืœื™ืฆื•ืจ ื˜ื‘ืœืื•ืช ืืœื” ื‘ืืžืฆืขื•ืช ืฉืื™ืœืชืช SQL ื”ื‘ืื”:

CREATE TABLE chat_data (
    chat_id BIGINT  PRIMARY KEY
                    UNIQUE,
    name    TEXT,
    age     INTEGER
);

CREATE TABLE chat_state (
    chat_id BIGINT PRIMARY KEY
                   UNIQUE,
    state   TEXT
);

ืื ื”ื•ืจื“ืชื ืืช ืคืจื•ื™ืงื˜ ื”ื‘ื•ื˜ ืž GitHub, ื•ืื– ื›ื“ื™ ืœื™ืฆื•ืจ ืืช ืžืกื“ ื”ื ืชื•ื ื™ื ืชื•ื›ืœ ืœื”ืฉืชืžืฉ ื‘ืงื•ื“ ื”ื‘ื ื‘-R.

# ะกะบั€ะธะฟั‚ ัะพะทะดะฐะฝะธั ะฑะฐะทั‹ ะดะฐะฝะฝั‹ั…
library(DBI)     # ะธะฝั‚ะตั€ั„ะตะนั ะดะปั ั€ะฐะฑะพั‚ั‹ ั ะกะฃะ‘ะ”
library(configr) # ั‡ั‚ะตะฝะธะต ะบะพะฝั„ะธะณะฐ
library(readr)   # ั‡ั‚ะตะฝะธะต ั‚ะตะบัั‚ะพะฒั‹ั… SQL ั„ะฐะนะปะพะฒ
library(RSQLite) # ะดั€ะฐะนะฒะตั€ ะดะปั ะฟะพะดะบะปัŽั‡ะตะฝะธั ะบ SQLite

# ะดะธั€ะตะบั‚ะพั€ะธั ะฟั€ะพะตะบั‚ะฐ
setwd(Sys.getenv('TG_BOT_PATH'))

# ั‡ั‚ะตะฝะธะต ะบะพะฝั„ะธะณะฐ
cfg <- read.config('config.cfg')

# ะฟะพะดะบะปัŽั‡ะตะฝะธะต ะบ SQLite
con <- dbConnect(SQLite(), cfg$db_settings$db_path)

# ะกะพะทะดะฐะฝะธะต ั‚ะฐะฑะปะธั† ะฒ ะฑะฐะทะต
dbExecute(con, statement = read_file('create_db_data.sql'))
dbExecute(con, statement = read_file('create_db_state.sql'))

ื›ืชื™ื‘ืช ืคื•ื ืงืฆื™ื•ืช ืœืขื‘ื•ื“ื” ืขื ืžืกื“ ื”ื ืชื•ื ื™ื

ื™ืฉ ืœื ื• ื›ื‘ืจ ืงื•ื‘ืฅ ืชืฆื•ืจื” ืžื•ื›ืŸ ื•ืžืกื“ ื ืชื•ื ื™ื ื ื•ืฆืจ. ื›ืขืช ืขืœื™ืš ืœื›ืชื•ื‘ ืคื•ื ืงืฆื™ื•ืช ื›ื“ื™ ืœืงืจื•ื ื•ืœื›ืชื•ื‘ ื ืชื•ื ื™ื ืœืžืกื“ ื ืชื•ื ื™ื ื–ื”.

ืื ื”ื•ืจื“ืชื ืืช ื”ืคืจื•ื™ืงื˜ ืž GitHub, ืื– ืชื•ื›ืœ ืœืžืฆื•ื ืืช ื”ืคื•ื ืงืฆื™ื•ืช ื‘ืงื•ื‘ืฅ db_bot_function.R.

ืงื•ื“ ืคื•ื ืงืฆื™ื” ืœืขื‘ื•ื“ื” ืขื ืžืกื“ ื”ื ืชื•ื ื™ื

# ###########################################################
# Function for work bot with database

# ะฟะพะปัƒั‡ะธั‚ัŒ ั‚ะตะบัƒั‰ะตะต ัะพัั‚ะพัะฝะธะต ั‡ะฐั‚ะฐ
get_state <- function(chat_id) {

  con <- dbConnect(SQLite(), cfg$db_settings$db_path)

  chat_state <- dbGetQuery(con, str_interp("SELECT state FROM chat_state WHERE chat_id == ${chat_id}"))$state

  return(unlist(chat_state))

  dbDisconnect(con)
}

# ัƒัั‚ะฐะฝะพะฒะธั‚ัŒ ั‚ะตะบัƒั‰ะตะต ัะพัั‚ะพัะฝะธะต ั‡ะฐั‚ะฐ
set_state <- function(chat_id, state) {

  con <- dbConnect(SQLite(), cfg$db_settings$db_path)

  # upsert ัะพัั‚ะพัะฝะธะต ั‡ะฐั‚ะฐ
  dbExecute(con, 
            str_interp("
            INSERT INTO chat_state (chat_id, state)
                VALUES(${chat_id}, '${state}') 
                ON CONFLICT(chat_id) 
                DO UPDATE SET state='${state}';
            ")
  )

  dbDisconnect(con)

}

# ะทะฐะฟะธััŒ ะฟะพะปัƒั‡ะตะฝะฝั‹ั… ะดะฐะฝะฝั‹ั… ะฒ ะฑะฐะทัƒ
set_chat_data <- function(chat_id, field, value) {

  con <- dbConnect(SQLite(), cfg$db_settings$db_path)

  # upsert ัะพัั‚ะพัะฝะธะต ั‡ะฐั‚ะฐ
  dbExecute(con, 
            str_interp("
            INSERT INTO chat_data (chat_id, ${field})
                VALUES(${chat_id}, '${value}') 
                ON CONFLICT(chat_id) 
                DO UPDATE SET ${field}='${value}';
            ")
  )

  dbDisconnect(con)

}

# read chat data
get_chat_data <- function(chat_id, field) {

  con <- dbConnect(SQLite(), cfg$db_settings$db_path)

  # upsert ัะพัั‚ะพัะฝะธะต ั‡ะฐั‚ะฐ
  data <- dbGetQuery(con, 
                     str_interp("
            SELECT ${field}
            FROM chat_data
            WHERE chat_id = ${chat_id};
            ")
  )

  dbDisconnect(con)

  return(data[[field]])

}

ื™ืฆืจื ื• 4 ืคื•ื ืงืฆื™ื•ืช ืคืฉื•ื˜ื•ืช:

  • get_state() - ืงื‘ืœ ืืช ืžืฆื‘ ื”ืฆ'ืื˜ ื”ื ื•ื›ื—ื™ ืžืžืกื“ ื”ื ืชื•ื ื™ื
  • set_state() - ื›ืชื•ื‘ ืืช ืžืฆื‘ ื”ืฆ'ืื˜ ื”ื ื•ื›ื—ื™ ืœืžืกื“ ื”ื ืชื•ื ื™ื
  • get_chat_data() - ืœืงื‘ืœ ื ืชื•ื ื™ื ืฉื ืฉืœื—ื• ืขืœ ื™ื“ื™ ื”ืžืฉืชืžืฉ
  • set_chat_data() - ืจืฉื•ื ื ืชื•ื ื™ื ืฉื”ืชืงื‘ืœื• ืžื”ืžืฉืชืžืฉ

ื›ืœ ื”ืคื•ื ืงืฆื™ื•ืช ื”ืŸ ื“ื™ ืคืฉื•ื˜ื•ืช, ื”ืŸ ืงื•ืจืื•ืช ื ืชื•ื ื™ื ืžืžืกื“ ื”ื ืชื•ื ื™ื ื‘ืืžืฆืขื•ืช ื”ืคืงื•ื“ื” dbGetQuery(), ืื• ืœื”ืชื—ื™ื™ื‘ UPSERT ืคืขื•ืœื” (ืฉื™ื ื•ื™ ื ืชื•ื ื™ื ืงื™ื™ืžื™ื ืื• ื›ืชื™ื‘ืช ื ืชื•ื ื™ื ื—ื“ืฉื™ื ืœืžืกื“ ื”ื ืชื•ื ื™ื), ื‘ืืžืฆืขื•ืช ื”ืคื•ื ืงืฆื™ื” dbExecute().

ื”ืชื—ื‘ื™ืจ ืขื‘ื•ืจ ืคืขื•ืœืช UPSERT ื”ื•ื ื›ื“ืœืงืžืŸ:

INSERT INTO chat_data (chat_id, ${field})
VALUES(${chat_id}, '${value}') 
ON CONFLICT(chat_id) 
DO UPDATE SET ${field}='${value}';

ื”ึธื”ึตืŸ. ื‘ืฉื“ื” ื”ื˜ื‘ืœืื•ืช ืฉืœื ื• chat_id ื™ืฉ ืื™ืœื•ืฅ ื™ื™ื—ื•ื“ื™ื•ืช ื•ื”ื•ื ื”ืžืคืชื— ื”ืขื™ืงืจื™ ืฉืœ ื˜ื‘ืœืื•ืช. ื‘ืชื—ื™ืœื”, ืื ื• ืžื ืกื™ื ืœื”ื•ืกื™ืฃ ืžื™ื“ืข ืœื˜ื‘ืœื”, ื•ืื ื• ืžืงื‘ืœื™ื ืฉื’ื™ืื” ืื โ€‹โ€‹ื ืชื•ื ื™ื ืขื‘ื•ืจ ื”ืฆ'ืื˜ ื”ื ื•ื›ื—ื™ ื›ื‘ืจ ืงื™ื™ืžื™ื, ื•ื‘ืžืงืจื” ื–ื” ืื ื• ืคืฉื•ื˜ ืžืขื“ื›ื ื™ื ืืช ื”ืžื™ื“ืข ืขื‘ื•ืจ ื”ืฆ'ืื˜ ื”ื–ื”.

ืœืื—ืจ ืžื›ืŸ, ื ืฉืชืžืฉ ื‘ืคื•ื ืงืฆื™ื•ืช ื”ืœืœื• ื‘ืฉื™ื˜ื•ืช ื•ื‘ืžืกื ื ื™ื ืฉืœ ื”ื‘ื•ื˜.

ืฉื™ื˜ื•ืช ื‘ื•ื˜

ื”ืฉืœื‘ ื”ื‘ื ื‘ื‘ื ื™ื™ืช ื”ื‘ื•ื˜ ืฉืœื ื• ื”ื•ื ื™ืฆื™ืจืช ืฉื™ื˜ื•ืช. ืื ื”ื•ืจื“ืชื ืืช ื”ืคืจื•ื™ืงื˜ ืž GitHub, ืื– ื›ืœ ื”ืฉื™ื˜ื•ืช ื ืžืฆืื•ืช ื‘ืงื•ื‘ืฅ bot_methods.R.

ืงื•ื“ ืฉื™ื˜ืช ื‘ื•ื˜

# ###########################################################
# bot methods

# start dialog
start <- function(bot, update) {

  # 

  # Send query
  bot$sendMessage(update$message$chat_id, 
                  text = "ะ’ะฒะตะดะธ ัะฒะพั‘ ะธะผั")

  # ะฟะตั€ะตะบะปัŽั‡ะฐะตะผ ัะพัั‚ะพัะฝะธะต ะดะธะฐะปะพะณะฐ ะฒ ั€ะตะถะธะผ ะพะถะธะดะฐะฝะธั ะฒะฒะพะดะฐ ะธะผะตะฝะธ
  set_state(chat_id = update$message$chat_id, state = 'wait_name')

}

# get current chat state
state <- function(bot, update) {

  chat_state <- get_state(update$message$chat_id)

  # Send state
  bot$sendMessage(update$message$chat_id, 
                  text = unlist(chat_state))

}

# reset dialog state
reset <- function(bot, update) {

  set_state(chat_id = update$message$chat_id, state = 'start')

}

# enter username
enter_name <- function(bot, update) {

  uname <- update$message$text

  # Send message with name
  bot$sendMessage(update$message$chat_id, 
                  text = paste0(uname, ", ะฟั€ะธัั‚ะฝะพ ะฟะพะทะฝะฐะบะพะผะธั‚ัั, ั ะฑะพั‚!"))

  # ะ—ะฐะฟะธัั‹ะฒะฐะตะผ ะธะผั ะฒ ะณะปะพะฑะฐะปัŒะฝัƒัŽ ะฟะตั€ะตะผะตะฝะฝัƒัŽ
  #username <<- uname
  set_chat_data(update$message$chat_id, 'name', uname) 

  # ะกะฟั€ะฐะฒัˆะธะฒะฐะตะผ ะฒะพะทั€ะฐัั‚
  bot$sendMessage(update$message$chat_id, 
                  text = "ะกะบะพะปัŒะบะพ ั‚ะตะฑะต ะปะตั‚?")

  # ะœะตะฝัะตะผ ัะพัั‚ะพัะฝะธะต ะฝะฐ ะพะถะธะดะฐะฝะธะต ะฒะฒะพะดะฐ ะธะผะตะฝะธ
  set_state(chat_id = update$message$chat_id, state = 'wait_age')

}

# enter user age
enter_age <- function(bot, update) {

  uage <- as.numeric(update$message$text)

  # ะฟั€ะพะฒะตั€ัะตะผ ะฑั‹ะปะพ ะฒะฒะตะดะตะฝะพ ั‡ะธัะปะพ ะธะปะธ ะฝะตั‚
  if ( is.na(uage) ) {

    # ะตัะปะธ ะฒะฒะตะดะตะฝะพ ะฝะต ั‡ะธัะปะพ ั‚ะพ ะฟะตั€ะตัะฟั€ะฐัˆะธะฒะฐะตะผ ะฒะพะทั€ะฐัั‚
    bot$sendMessage(update$message$chat_id, 
                    text = "ะขั‹ ะฒะฒั‘ะป ะฝะตะบะพั€ั€ะตะบั‚ะฝั‹ะต ะดะฐะฝะฝั‹ะต, ะฒะฒะตะดะธ ั‡ะธัะปะพ")

  } else {

    # ะตัะปะธ ะฒะฒะตะดะตะฝะพ ั‡ะธัะปะพ ัะพะพะฑั‰ะฐะตะผ ั‡ั‚ะพ ะฒะพะทั€ะฐัั‚ ะฟั€ะธะฝัั‚
    bot$sendMessage(update$message$chat_id, 
                    text = "ะžะš, ะฒะพะทั€ะฐัั‚ ะฟั€ะธะฝัั‚")

    # ะทะฐะฟะธัั‹ะฒะฐะตะผ ะณะปะพะฑะฐะปัŒะฝัƒัŽ ะฟะตั€ะตะผะตะฝะฝัƒัŽ ั ะฒะพะทั€ะฐัั‚ะพะผ
    #userage <<- uage
    set_chat_data(update$message$chat_id, 'age', uage) 

    # ัะพะพะฑั‰ะฐะตะผ ะบะฐะบะธะต ะดะฐะฝะฝั‹ะต ะฑั‹ะปะธ ัะพะฑั€ะฐะฝั‹
    username <- get_chat_data(update$message$chat_id, 'name')
    userage  <- get_chat_data(update$message$chat_id, 'age')

    bot$sendMessage(update$message$chat_id, 
                    text = paste0("ะขะตะฑั ะทะพะฒัƒั‚ ", username, " ะธ ั‚ะตะฑะต ", userage, " ะปะตั‚. ะ‘ัƒะดะตะผ ะทะฝะฐะบะพะผั‹"))

    # ะฒะพะทะฒั€ะฐั‰ะฐะตะผ ะดะธะฐะปะพะณ ะฒ ะธัั…ะพะดะฝะพะต ัะพัั‚ะพัะฝะธะต
    set_state(chat_id = update$message$chat_id, state = 'start')
  }

}

ื™ืฆืจื ื• 5 ืฉื™ื˜ื•ืช:

  • ื”ืชื—ืœ โ€” ื”ืชื—ืœ ืชื™ื‘ืช ื“ื•-ืฉื™ื—
  • ืžืฆื‘ โ€” ืงื‘ืœ ืืช ืžืฆื‘ ื”ืฆ'ืื˜ ื”ื ื•ื›ื—ื™
  • ืื™ืคื•ืก โ€” ืืคืก ืืช ืžืฆื‘ ื”ืฆ'ืื˜ ื”ื ื•ื›ื—ื™
  • enter_name - ื”ื‘ื•ื˜ ืžื‘ืงืฉ ืืช ืฉืžืš
  • enter_age โ€” ื”ื‘ื•ื˜ ืฉื•ืืœ ืืช ื”ื’ื™ืœ ืฉืœืš

ืฉื™ื˜ื” start ืžื‘ืงืฉ ืืช ืฉืžืš, ื•ืžืฉื ื” ืืช ืžืฆื‘ ื”ืฆ'ืื˜ ืœ wait_name, ื›ืœื•ืžืจ ืœืžืฆื‘ ื”ืžืชื ื” ืœื”ื–ื ืช ืฉืžืš.

ืœืื—ืจ ืžื›ืŸ, ืืชื” ืฉื•ืœื— ืืช ื”ืฉื ื•ื”ื•ื ืžืขื•ื‘ื“ ื‘ืฉื™ื˜ื” enter_name, ื”ื‘ื•ื˜ ืžื‘ืจืš ืื•ืชืš, ื›ื•ืชื‘ ืืช ื”ืฉื ืฉื”ืชืงื‘ืœ ืœืžืกื“ ื”ื ืชื•ื ื™ื, ื•ืžืขื‘ื™ืจ ืืช ื”ืฆ'ืื˜ ืœืžืฆื‘ wait_age.

ื‘ืฉืœื‘ ื–ื”, ื”ื‘ื•ื˜ ืžืฆืคื” ืžืžืš ืœื”ื–ื™ืŸ ืืช ื”ื’ื™ืœ ืฉืœืš. ืืชื” ืฉื•ืœื— ืืช ื”ื’ื™ืœ ืฉืœืš, ื”ื‘ื•ื˜ ื‘ื•ื“ืง ืืช ื”ื”ื•ื“ืขื”, ืื ืฉืœื—ืช ื˜ืงืกื˜ ื‘ืžืงื•ื ืžืกืคืจ, ื”ื•ื ื™ื’ื™ื“: ะขั‹ ะฒะฒั‘ะป ะฝะตะบะพั€ั€ะตะบั‚ะฝั‹ะต ะดะฐะฝะฝั‹ะต, ะฒะฒะตะดะธ ั‡ะธัะปะพ, ื•ื™ืžืชื™ืŸ ืฉืชื–ื™ืŸ ืžื—ื“ืฉ ืืช ื”ื ืชื•ื ื™ื ืฉืœืš. ืื ืฉืœื—ืช ืžืกืคืจ, ื”ื‘ื•ื˜ ื™ื“ื•ื•ื— ืฉื”ื•ื ืงื™ื‘ืœ ืืช ื’ื™ืœืš, ื™ื›ืชื•ื‘ ืืช ื”ื ืชื•ื ื™ื ืฉื”ืชืงื‘ืœื• ืœืžืื’ืจ, ื™ื“ื•ื•ื— ืขืœ ื›ืœ ื”ื ืชื•ื ื™ื ืฉื”ืชืงื‘ืœื• ืžืžืš ื•ื™ื—ื–ื™ืจ ืืช ืžืฆื‘ ื”ืฆ'ืื˜ ืœืžืงื•ืžื• ื”ืžืงื•ืจื™, ื›ืœื•ืžืจ. V start.

ืขืœ ื™ื“ื™ ืงืจื™ืื” ืœืฉื™ื˜ื” state ืืชื” ื™ื›ื•ืœ ืœื‘ืงืฉ ืืช ืกื˜ื˜ื•ืก ื”ืฆ'ืื˜ ื”ื ื•ื›ื—ื™ ื‘ื›ืœ ืขืช, ื•ื‘ืืžืฆืขื•ืช reset ืœื”ื—ื–ื™ืจ ืืช ื”ืฆ'ืื˜ ืœืžืฆื‘ื• ื”ืžืงื•ืจื™.

ืžืกื ื ื™ ื”ื•ื“ืขื•ืช

ื‘ืžืงืจื” ืฉืœื ื•, ื–ื”ื• ืื—ื“ ื”ื—ืœืงื™ื ื”ื—ืฉื•ื‘ื™ื ื‘ื™ื•ืชืจ ื‘ื‘ื ื™ื™ืช ื‘ื•ื˜. ื‘ืขื–ืจืช ืžืกื ื ื™ ืžืกืจื™ื ื”ื‘ื•ื˜ ื™ื‘ื™ืŸ ืื™ื–ื” ืžื™ื“ืข ื”ื•ื ืžืฆืคื” ืžืžืš ื•ื›ื™ืฆื“ ื™ืฉ ืœืขื‘ื“ ืื•ืชื•.

ื‘ืคืจื•ื™ืงื˜ ืขืœ GitHub ืžืกื ื ื™ื ืจืฉื•ืžื™ื ื‘ืงื•ื‘ืฅ message_filters.R.

ืงื•ื“ ืžืกื ืŸ ื”ื•ื“ืขื”:

# ###########################################################
# message state filters

# ั„ะธะปัŒั‚ั€ ัะพะพะฑั‰ะตะฝะธะน ะฒ ัะพัั‚ะพัะฝะธะธ ะพะถะธะดะฐะฝะธั ะธะผะตะฝะธ
MessageFilters$wait_name <- BaseFilter(function(message) {
  get_state( message$chat_id )  == "wait_name"
}
)

# ั„ะธะปัŒั‚ั€ ัะพะพะฑั‰ะตะฝะธะน ะฒ ัะพัั‚ะพัะฝะธะธ ะพะถะธะดะฐะฝะธั ะฒะพะทั€ะฐัั‚ะฐ
MessageFilters$wait_age <- BaseFilter(function(message) {
  get_state( message$chat_id )   == "wait_age"
}
)

ื‘ืžืกื ื ื™ื ืื ื• ืžืฉืชืžืฉื™ื ื‘ืคื•ื ืงืฆื™ื” ืฉื ื›ืชื‘ื” ืงื•ื“ื ืœื›ืŸ get_state(), ื›ื“ื™ ืœื‘ืงืฉ ืืช ื”ืžืฆื‘ ื”ื ื•ื›ื—ื™ ืฉืœ ื”ืฆ'ืื˜. ืคื•ื ืงืฆื™ื” ื–ื• ื“ื•ืจืฉืช ืจืง ืืจื’ื•ืžื ื˜ ืื—ื“, ืžื–ื”ื” ืฆ'ืื˜.

ื”ืžืกื ืŸ ื”ื‘ื wait_name ืžืขื‘ื“ ื”ื•ื“ืขื•ืช ื›ืืฉืจ ื”ืฆ'ืื˜ ื ืžืฆื ื‘ืžืฆื‘ wait_name, ื•ื‘ื”ืชืื ืœื›ืš ื”ืคื™ืœื˜ืจ wait_age ืžืขื‘ื“ ื”ื•ื“ืขื•ืช ื›ืืฉืจ ื”ืฆ'ืื˜ ื ืžืฆื ื‘ืžืฆื‘ wait_age.

ืžื˜ืคืœื™ื

ื”ืงื•ื‘ืฅ ืขื ื”ืžื˜ืคืœื™ื ื ืงืจื ืžื˜ืคืœื™ื.ืจ, ื•ื™ืฉ ืœื• ืืช ื”ืงื•ื“ ื”ื‘ื:

# ###########################################################
# handlers

# command handlers
start_h <- CommandHandler('start', start)
state_h <- CommandHandler('state', state)
reset_h <- CommandHandler('reset', reset)

# message handlers
## !MessageFilters$command - ะพะทะฝะฐั‡ะฐะตั‚ ั‡ั‚ะพ ะบะพะผะฐะฝะดั‹ ะดะฐะฝะฝั‹ะต ะพะฑั€ะฐะฑะพั‚ั‡ะธะบะธ ะฝะต ะพะฑั€ะฐะฑะฐั‚ั‹ะฒะฐัŽั‚, 
## ั‚ะพะปัŒะบะพ ั‚ะตะบัั‚ะพะฒั‹ะต ัะพะพะฑั‰ะตะฝะธั
wait_age_h  <- MessageHandler(enter_age,  MessageFilters$wait_age  & !MessageFilters$command)
wait_name_h <- MessageHandler(enter_name, MessageFilters$wait_name & !MessageFilters$command)

ืจืืฉื™ืช ืื ื• ื™ื•ืฆืจื™ื ืžื˜ืคืœื™ ืคืงื•ื“ื•ืช ืฉื™ืืคืฉืจื• ืœืš ืœื”ืจื™ืฅ ืฉื™ื˜ื•ืช ืœื”ืคืขืœืช ื“ื•-ืฉื™ื—, ืœืืคืก ืื•ืชื” ื•ืœืฉืื•ืœ ืืช ื”ืžืฆื‘ ื”ื ื•ื›ื—ื™.

ืœืื—ืจ ืžื›ืŸ, ืื ื• ื™ื•ืฆืจื™ื 2 ืžื˜ืคืœื™ ื”ื•ื“ืขื•ืช ื‘ืืžืฆืขื•ืช ื”ืžืกื ื ื™ื ืฉื ื•ืฆืจื• ื‘ืฉืœื‘ ื”ืงื•ื“ื, ื•ืžื•ืกื™ืคื™ื ืœื”ื ืคื™ืœื˜ืจ !MessageFilters$command, ื›ื“ื™ ืฉื ื•ื›ืœ ืœื”ืฉืชืžืฉ ื‘ืคืงื•ื“ื•ืช ื‘ื›ืœ ืžืฆื‘ ืฆ'ืื˜.

ืงื•ื“ ื”ืฉืงืช ื‘ื•ื˜

ื›ืขืช ื™ืฉ ืœื ื• ื”ื›ืœ ืžื•ื›ืŸ ืœื”ืคืขืœื”, ื”ืงื•ื“ ื”ืจืืฉื™ ืœื”ืคืขืœืช ื”ื‘ื•ื˜ ื ืžืฆื ื‘ืงื•ื‘ืฅ bot.R.

library(telegram.bot)
library(tidyverse)
library(RSQLite)
library(DBI)
library(configr)

# ะฟะตั€ะตั…ะพะดะธะผ ะฒ ะฟะฐะฟะบัƒ ะฟั€ะพะตะบั‚ะฐ
setwd(Sys.getenv('TG_BOT_PATH'))

# ั‡ะธั‚ะฐะตะผ ะบะพะฝั„ะธะณ
cfg <- read.config('config.cfg')

# ัะพะทะดะฐั‘ะผ ัะบะทะตะผะฟะปัั€ ะฑะพั‚ะฐ
updater <- Updater(cfg$bot_settings$bot_token)

# ะ—ะฐะณั€ัƒะทะบะฐ ะบะพะผะฟะพะฝะตะฝั‚ะพะฒ ะฑะพั‚ะฐ
source('db_bot_function.R') # ั„ัƒะฝะบั†ะธะธ ะดะปั ั€ะฐะฑะพั‚ั‹ ั ะ‘ะ”
source('bot_methods.R')     # ะผะตั‚ะพะดั‹ ะฑะพั‚ะฐ
source('message_filters.R') # ั„ะธะปัŒั‚ั€ั‹ ัะพะพะฑั‰ะตะฝะธะน
source('handlers.R') # ะพะฑั€ะฐะฑะพั‚ั‡ะธะบะธ ัะพะพะฑั‰ะตะฝะธะน

# ะ”ะพะฑะฐะฒะปัะตะผ ะพะฑั€ะฐะฑะพั‚ั‡ะธะบะธ ะฒ ะดะธัะฟะตั‚ั‡ะตั€
updater <- updater +
  start_h +
  wait_age_h +
  wait_name_h +
  state_h +
  reset_h

# ะ—ะฐะฟัƒัะบะฐะตะผ ะฑะพั‚ะฐ
updater$start_polling()

ื›ืชื•ืฆืื” ืžื›ืš, ืงื™ื‘ืœื ื• ืืช ื”ื‘ื•ื˜ ื”ื–ื”:
ื›ืชื™ื‘ืช ื‘ื•ื˜ ื˜ืœื’ืจื ื‘-R (ื—ืœืง 4): ื‘ื ื™ื™ืช ื“ื™ืืœื•ื’ ืขืงื‘ื™ ื•ื”ื’ื™ื•ื ื™ ืขื ื”ื‘ื•ื˜

ื‘ื›ืœ ืขืช ื‘ืืžืฆืขื•ืช ื”ืคืงื•ื“ื” /state ืื ื• ื™ื›ื•ืœื™ื ืœืฉืื•ืœ ืืช ืžืฆื‘ ื”ืฆ'ืื˜ ื”ื ื•ื›ื—ื™, ื•ื‘ืืžืฆืขื•ืช ื”ืคืงื•ื“ื” /reset ืœื”ื—ื–ื™ืจ ืืช ื”ืฆ'ืื˜ ืœืžืฆื‘ื• ื”ืžืงื•ืจื™ ื•ืœื”ืชื—ื™ืœ ืืช ื”ื“ื™ืืœื•ื’ ืฉื•ื‘.

ืžืกืงื ื”

ื‘ืžืืžืจ ื–ื”, ื”ื‘ื ื• ื›ื™ืฆื“ ืœื”ืฉืชืžืฉ ื‘ืžืกื“ ื ืชื•ื ื™ื ื‘ืชื•ืš ื‘ื•ื˜, ื•ื›ื™ืฆื“ ืœื‘ื ื•ืช ื“ื™ืืœื•ื’ื™ื ืœื•ื’ื™ื™ื ืขื•ืงื‘ื™ื ืขืœ ื™ื“ื™ ื”ืงืœื˜ืช ืžืฆื‘ ื”ืฆ'ืื˜.

ื‘ืžืงืจื” ื–ื”, ื”ืกืชื›ืœื ื• ืขืœ ื”ื“ื•ื’ืžื” ื”ืคืจื™ืžื™ื˜ื™ื‘ื™ืช ื‘ื™ื•ืชืจ, ื›ื“ื™ ืฉื™ื”ื™ื” ืœืš ืงืœ ื™ื•ืชืจ ืœื”ื‘ื™ืŸ ืืช ื”ืจืขื™ื•ืŸ ืฉืœ ื‘ื ื™ื™ืช ื‘ื•ื˜ื™ื ื›ืืœื”; ื‘ืคื•ืขืœ, ืืชื” ื™ื›ื•ืœ ืœื‘ื ื•ืช ื“ื™ืืœื•ื’ื™ื ื”ืจื‘ื” ื™ื•ืชืจ ืžื•ืจื›ื‘ื™ื.

ื‘ืžืืžืจ ื”ื‘ื ื‘ืกื“ืจื” ื–ื• ื ืœืžื“ ื›ื™ืฆื“ ืœื”ื’ื‘ื™ืœ ืืช ื”ื–ื›ื•ื™ื•ืช ืฉืœ ืžืฉืชืžืฉื™ ื”ื‘ื•ื˜ ืœื”ืฉืชืžืฉ ื‘ืฉื™ื˜ื•ืช ืฉื•ื ื•ืช ืฉืœื•.

ืžืงื•ืจ: www.habr.com

ื”ื•ืกืคืช ืชื’ื•ื‘ื”