Skip to content

Instantly share code, notes, and snippets.

@antimech
Last active November 22, 2023 09:14
Show Gist options
  • Save antimech/9133c7b97d0e4ca2ce90d8910306a4d2 to your computer and use it in GitHub Desktop.
Save antimech/9133c7b97d0e4ca2ce90d8910306a4d2 to your computer and use it in GitHub Desktop.
Здесь фрагменты кода в одном из старых проектов под NDA (к которому есть доступ из дома) опубликованные с разрешения их владельцев, здесь только начинаю применять сервис-классы. Проект сделан на Laravel + Botman.
<?php
use App\Http\Controllers\BotManController;
use Illuminate\Support\Facades\Log;
$botman = resolve('botman');
$botman->hears('/start', BotManController::class . '@welcome');
$botman->hears('Add', BotManController::class . '@createAlert');
$botman->hears('Show', BotManController::class . '@showAlerts');
$botman->hears('Remove', BotManController::class . '@removeAlert');
// Cancel any conversation
$botman->hears('/stop', function ($bot) {
$bot->reply('Stopped.');
})->stopsConversation();
// Handle incorrect commands
$botman->fallback(function ($bot) {
$bot->reply('Sorry, I did not understand these commands.');
});
// Handle exceptions
$botman->exception(Exception::class, function ($exception, $bot) {
Log::error($exception);
$bot->reply('Sorry, something went wrong. Try again later, please.');
});
<?php
namespace App\Http\Controllers;
use App\Conversations\AddAlertConversation;
use App\Conversations\RemoveAlertConversation;
use App\Services\TimeGapService;
use App\TelegramUser;
use BotMan\BotMan\BotMan;
use Illuminate\Database\Eloquent\ModelNotFoundException;
class BotManController extends Controller
{
public function __construct(
protected TimeGapService $timeGapService
) {}
/**
* Place your BotMan logic here.
*/
public function handle(): void
{
$botman = app('botman');
$botman->listen();
}
/**
* Loaded through routes/botman.php
*/
public function welcome(BotMan $bot): void
{
$botUser = $bot->getUser();
$telegramUser = TelegramUser::updateOrCreate(['id' => $botUser->getId()], [
'first_name' => $botUser->getFirstName(),
'last_name' => $botUser->getLastName(),
'username' => $botUser->getUsername(),
]);
// Fix for failing BotMan test
if (app()->runningUnitTests() === false) {
$telegramUser->update([
'language_code' => $botUser->getLanguageCode(),
]);
}
$menuKeyboard = [
['Add', 'Show'],
['Remove'],
];
$bot->reply('Welcome! What do you want to do with your alerts?', [
'reply_markup' => json_encode([
'keyboard' => $menuKeyboard,
'one_time_keyboard' => false,
'resize_keyboard' => true
])
]);
}
/**
* Create an alert.
*/
public function createAlert(BotMan $bot): void
{
$bot->startConversation(new AddAlertConversation);
}
/**
* Show user's alerts.
*/
public function showAlerts(BotMan $bot): mixed
{
$telegramUserId = $bot->getUser()->getId();
try {
$telegramUser = TelegramUser::findOrFail($telegramUserId);
} catch (ModelNotFoundException) {
return $bot->reply('Restart the bot, please: /start');
}
$alerts = $telegramUser->alerts;
if ($alerts->isEmpty()) {
return $bot->reply('You don\'t have any alerts yet.');
}
$message = "*Ticker | Percent | Timeframe*\n";
foreach ($alerts as $alert) {
$humanReadableTimeframe = $this->timeGapService->secondsToTimeframe($alert->timeframe);
$message .= "{$alert->coinPair->ticker} | {$alert->value_change_percent}% | $humanReadableTimeframe\n";
}
return $bot->reply($message, [
'parse_mode' => 'Markdown'
]);
}
/**
* Delete specific alert.
*/
public function removeAlert(BotMan $bot): void
{
$bot->startConversation(new RemoveAlertConversation);
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Alert extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'value_change_percent', 'timeframe', 'recent_alerts_count', 'triggered_at',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'triggered_at' => 'datetime',
];
/**
* Get the coin pair to which this alert applies.
*/
public function coinPair(): BelongsTo
{
return $this->belongsTo(CoinPair::class);
}
/**
* Get the user who subscribed to this alert.
*/
public function subscriber(): BelongsTo
{
return $this->belongsTo(TelegramUser::class, 'telegram_user_id');
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Coin extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'code',
];
public function basePairs(): HasMany
{
return $this->hasMany(CoinPair::class, 'base_id');
}
public function quotedPairs(): HasMany
{
return $this->hasMany(CoinPair::class, 'quoted_id');
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class CoinPair extends Model
{
/**
* Get the base coin.
*/
public function baseCoin(): BelongsTo
{
return $this->belongsTo(Coin::class, 'base_id');
}
/**
* Get the quoted coin.
*/
public function quotedCoin(): BelongsTo
{
return $this->belongsTo(Coin::class, 'quoted_id');
}
/**
* Get value history of the coin pair.
*/
public function values(): HasMany
{
return $this->hasMany(CoinPairValue::class);
}
/**
* Get previous value of the coin pair.
*/
public function getPreviousValueAttribute(): float {
if ($this->values()->count() < 2) {
return false;
}
return $this->values()->latest()->skip(1)->take(1)->first()->value;
}
/**
* Get current value of the coin pair.
*/
public function getValueAttribute(): float
{
if ($this->values()->count() === 0) {
return false;
}
return $this->values()->latest()->first()->value;
}
/**
* Get the coin pair's ticker.
*/
public function getTickerAttribute(): string
{
return "{$this->baseCoin->code}/{$this->quotedCoin->code}";
}
/**
* Get alerts related to this coin pair.
*/
public function alerts(): HasMany
{
return $this->hasMany(Alert::class);
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class CoinPairValue extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'value',
];
/**
* Get the coin pair to which this value applies.
*/
public function coinPair(): BelongsTo
{
return $this->belongsTo(CoinPair::class);
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class TelegramUser extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'id', 'first_name', 'last_name', 'username', 'language_code',
];
/**
* Get user's alerts.
*/
public function alerts(): HasMany
{
return $this->hasMany(Alert::class);
}
}
<?php
namespace App\Services;
use Carbon\Carbon;
class TimeGapService
{
/**
* Translate human-readable timeframe (from "1m" up to "24h") into seconds.
*/
public function recognizeTimeframeString(string $timeframe): bool|int
{
// Validate timeframe string
if (!preg_match('/^([1-9]|1[0-9]|2[0-4])(h)|([1-9]|[1-5][0-9])(m)$/', $timeframe)) {
return false;
}
// Recognize timeframe
$timeframeShortcuts = ['m', 'h'];
$timeframeWords = ['minutes', 'hours'];
$timeframeToParse = str_replace($timeframeShortcuts, $timeframeWords, $timeframe);
$parsedTimeframe = Carbon::parse($timeframeToParse)->diffInSeconds() + 1;
if ($timeframeToParse === '24hours') {
$parsedTimeframe = 86400;
}
return $parsedTimeframe;
}
/**
* Translate seconds into human-readable timeframe.
*/
public function secondsToTimeframe(int $seconds): string
{
$timeframeMinutes = $seconds / 60;
$timeframeHours = $timeframeMinutes / 60;
if ($timeframeHours >= 1) {
$humanReadableTimeframe = "{$timeframeHours}h";
} else {
$humanReadableTimeframe = "{$timeframeMinutes}m";
}
return $humanReadableTimeframe;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment