Telegram
コミュニケーションツール
Telegram
概要
Telegramは、セキュリティとプライバシーを重視したクラウドベースのメッセージングアプリです。エンドツーエンド暗号化、大容量ファイル送信、Bot API、ミニアプリ開発をサポートし、最大20万人のスーパーグループと無制限のチャンネル購読者数を提供します。オープンソースでクロスプラットフォーム対応、高速な同期機能が特徴です。
詳細
Telegram(テレグラム)は、2013年にロシア出身の起業家Pavel Durovによって設立されたクラウドベースのメッセージングプラットフォームです。現在、世界中で8億人以上のアクティブユーザーを持ち、プライバシーと自由な情報共有を重視するユーザーに支持されています。特にセキュリティとオープンソースへのコミットメントで知られています。
2024-2025年には、Mini Apps(ミニアプリ)のフルスクリーン表示、メディア共有機能、位置情報アクセス、DeviceStorage・SecureStorage API、ビジネスアカウント向けAPI、Telegram Premium機能強化、Webhook改善、チャンネル管理API拡充などの大幅な機能強化が行われました。
Telegram Bot APIは包括的なBot開発機能を提供し、Webhook統合、インラインキーボード、ファイル操作、支払い処理、ゲーム開発、ミニアプリ開発が可能です。また、MTProto protocolによる高度なセキュリティと、Telegram Cloudによる無制限ストレージを特徴としています。
メリット・デメリット
メリット
- 強力なセキュリティ: エンドツーエンド暗号化(Secret Chat)、MTProto protocol
- 豊富なBot API: 包括的なBot開発機能、ミニアプリ開発サポート
- 大容量ファイル送信: 最大2GBのファイル送信、無制限クラウドストレージ
- 大規模グループ: 最大20万人のスーパーグループ、無制限チャンネル
- クロスプラットフォーム: 全プラットフォーム対応、リアルタイム同期
- オープンソース: クライアントアプリはオープンソース
- 無料利用: 基本機能は完全無料、広告なし
- 高速性: 軽量設計と高速メッセージ配信
デメリット
- 匿名性の問題: 電話番号が必要、完全匿名ではない
- プライバシー懸念: 一部の国での規制や監視の可能性
- エンタープライズ機能不足: ビジネス向け機能の制限
- サポート体制: 公式サポートの限界
- ガバナンス: 中央集権的な運営体制
- デフォルト暗号化: 通常チャットはデフォルトで暗号化されない
主要リンク
- Telegram公式サイト
- Telegram Bot API
- Telegram開発者向けドキュメント
- ミニアプリ開発ガイド
- MTProto Protocol
- Telegram Desktop(オープンソース)
- Bot Platform
書き方の例
Python-telegram-bot を使用したBot開発
# python-telegram-bot v20.x を使用したTelegram Bot実装
import asyncio
import logging
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, WebAppInfo
from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes, CallbackQueryHandler
# ログ設定
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
logger = logging.getLogger(__name__)
class TelegramBot:
def __init__(self, token: str):
self.application = Application.builder().token(token).build()
self.setup_handlers()
def setup_handlers(self):
"""ハンドラーの設定"""
# コマンドハンドラー
self.application.add_handler(CommandHandler("start", self.start_command))
self.application.add_handler(CommandHandler("help", self.help_command))
self.application.add_handler(CommandHandler("weather", self.weather_command))
self.application.add_handler(CommandHandler("task", self.task_command))
self.application.add_handler(CommandHandler("miniapp", self.miniapp_command))
# メッセージハンドラー
self.application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, self.echo_message))
self.application.add_handler(MessageHandler(filters.PHOTO, self.photo_handler))
self.application.add_handler(MessageHandler(filters.Document.ALL, self.document_handler))
# コールバッククエリハンドラー
self.application.add_handler(CallbackQueryHandler(self.button_handler))
async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""スタートコマンドの処理"""
user = update.effective_user
welcome_text = f"こんにちは、{user.first_name}さん!🚀\n\nTelegram Botへようこそ。\n利用可能なコマンド:\n/help - ヘルプを表示\n/weather - 天気情報\n/task - タスク管理\n/miniapp - ミニアプリを開く"
# インラインキーボードの作成
keyboard = [
[InlineKeyboardButton("🌤️ 天気", callback_data='weather_tokyo')],
[InlineKeyboardButton("📋 タスク作成", callback_data='create_task')],
[InlineKeyboardButton("🚀 ミニアプリ", web_app=WebAppInfo(url="https://your-webapp.com"))],
[InlineKeyboardButton("ℹ️ ヘルプ", callback_data='help')]
]
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text(welcome_text, reply_markup=reply_markup)
async def help_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""ヘルプコマンドの処理"""
help_text = """
🤖 **Bot Commands**
**基本コマンド:**
/start - Botを開始
/help - このヘルプを表示
**機能コマンド:**
/weather [都市名] - 天気情報を取得
/task [タスク名] - タスクを作成
/miniapp - ミニアプリを起動
**ファイル機能:**
- 画像送信で自動OCR処理
- ドキュメント送信で分析
- 音声メッセージ対応
**特殊機能:**
- インラインボタン操作
- Webアプリ統合
- 支払い処理(Telegram Payments)
"""
await update.message.reply_text(help_text, parse_mode='Markdown')
async def weather_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""天気コマンドの処理"""
# 引数から都市名を取得(デフォルト: Tokyo)
city = ' '.join(context.args) if context.args else 'Tokyo'
try:
# 外部天気APIを呼び出し(例:OpenWeatherMap)
weather_data = await self.get_weather_info(city)
if weather_data:
weather_text = f"""
🌤️ **{weather_data['name']}の天気**
天気: {weather_data['weather']}
気温: {weather_data['temperature']}°C
湿度: {weather_data['humidity']}%
風速: {weather_data['wind_speed']} m/s
更新時刻: {weather_data['updated_at']}
"""
# インラインキーボードで他の都市の天気も表示
keyboard = [
[InlineKeyboardButton("🗾 東京", callback_data='weather_tokyo'),
InlineKeyboardButton("🗽 ニューヨーク", callback_data='weather_newyork')],
[InlineKeyboardButton("🗼 ロンドン", callback_data='weather_london'),
InlineKeyboardButton("🥖 パリ", callback_data='weather_paris')]
]
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text(weather_text, reply_markup=reply_markup, parse_mode='Markdown')
else:
await update.message.reply_text(f"❌ {city}の天気情報を取得できませんでした。")
except Exception as e:
logger.error(f"Weather API error: {e}")
await update.message.reply_text("❌ 天気情報の取得中にエラーが発生しました。")
async def task_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""タスクコマンドの処理"""
if context.args:
task_title = ' '.join(context.args)
# タスクをデータベースまたはファイルに保存
task_id = await self.save_task(task_title, update.effective_user.id)
task_text = f"""
✅ **タスクが作成されました**
タスクID: {task_id}
タイトル: {task_title}
作成者: {update.effective_user.first_name}
作成日時: {asyncio.get_event_loop().time()}
"""
# タスク管理用インラインキーボード
keyboard = [
[InlineKeyboardButton("✅ 完了", callback_data=f'complete_task_{task_id}')],
[InlineKeyboardButton("✏️ 編集", callback_data=f'edit_task_{task_id}')],
[InlineKeyboardButton("📋 タスク一覧", callback_data='list_tasks')]
]
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text(task_text, reply_markup=reply_markup, parse_mode='Markdown')
else:
await update.message.reply_text("タスク名を指定してください。\n例: /task 買い物に行く")
async def miniapp_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""ミニアプリコマンドの処理"""
# ミニアプリ用のインラインキーボード
keyboard = [
[InlineKeyboardButton("🎮 ゲーム", web_app=WebAppInfo(url="https://your-game-app.com"))],
[InlineKeyboardButton("📊 ダッシュボード", web_app=WebAppInfo(url="https://your-dashboard-app.com"))],
[InlineKeyboardButton("🛒 ショップ", web_app=WebAppInfo(url="https://your-shop-app.com"))]
]
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text(
"🚀 **ミニアプリを選択してください:**\n\n下のボタンからアプリを起動できます。",
reply_markup=reply_markup,
parse_mode='Markdown'
)
async def echo_message(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""通常メッセージの処理"""
message_text = update.message.text.lower()
# キーワード検出と自動応答
if 'こんにちは' in message_text or 'hello' in message_text:
await update.message.reply_text(f"こんにちは、{update.effective_user.first_name}さん!😊")
elif 'ありがとう' in message_text or 'thanks' in message_text:
await update.message.reply_text("どういたしまして!🙏")
elif 'help' in message_text or 'ヘルプ' in message_text:
await self.help_command(update, context)
else:
# エコー機能
await update.message.reply_text(f"メッセージを受信しました: {update.message.text}")
async def photo_handler(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""画像メッセージの処理"""
photo = update.message.photo[-1] # 最高解像度の画像を取得
# ファイルをダウンロード
file = await context.bot.get_file(photo.file_id)
file_path = f"downloads/{photo.file_id}.jpg"
await file.download_to_drive(file_path)
# OCR処理(例)
try:
extracted_text = await self.extract_text_from_image(file_path)
response_text = f"""
📸 **画像を受信しました**
ファイルサイズ: {photo.file_size} bytes
OCR結果:
{extracted_text if extracted_text else '文字は検出されませんでした'}
"""
await update.message.reply_text(response_text, parse_mode='Markdown')
except Exception as e:
logger.error(f"Image processing error: {e}")
await update.message.reply_text("❌ 画像処理中にエラーが発生しました。")
async def document_handler(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""ドキュメントメッセージの処理"""
document = update.message.document
# ファイルサイズチェック(50MB制限)
if document.file_size > 50 * 1024 * 1024:
await update.message.reply_text("❌ ファイルサイズが大きすぎます(50MB以下にしてください)。")
return
# ファイルをダウンロード
file = await context.bot.get_file(document.file_id)
file_path = f"downloads/{document.file_name}"
await file.download_to_drive(file_path)
response_text = f"""
📄 **ドキュメントを受信しました**
ファイル名: {document.file_name}
サイズ: {document.file_size} bytes
MIME タイプ: {document.mime_type}
ファイルは正常に保存されました。
"""
# ファイルタイプに応じた処理オプション
keyboard = []
if document.mime_type == 'text/plain':
keyboard.append([InlineKeyboardButton("📖 テキスト解析", callback_data=f'analyze_text_{document.file_id}')])
elif document.mime_type.startswith('image/'):
keyboard.append([InlineKeyboardButton("🔍 画像解析", callback_data=f'analyze_image_{document.file_id}')])
reply_markup = InlineKeyboardMarkup(keyboard) if keyboard else None
await update.message.reply_text(response_text, reply_markup=reply_markup, parse_mode='Markdown')
async def button_handler(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""インラインボタンの処理"""
query = update.callback_query
await query.answer()
callback_data = query.data
if callback_data.startswith('weather_'):
city = callback_data.replace('weather_', '')
weather_data = await self.get_weather_info(city)
if weather_data:
await query.edit_message_text(
f"🌤️ **{weather_data['name']}の天気**\n\n"
f"天気: {weather_data['weather']}\n"
f"気温: {weather_data['temperature']}°C",
parse_mode='Markdown'
)
elif callback_data == 'create_task':
await query.edit_message_text(
"📋 タスクを作成するには以下のコマンドを使用してください:\n\n"
"/task [タスク名]\n\n"
"例: /task 買い物に行く"
)
elif callback_data.startswith('complete_task_'):
task_id = callback_data.replace('complete_task_', '')
await self.complete_task(task_id)
await query.edit_message_text(f"✅ タスク {task_id} が完了しました!")
elif callback_data == 'help':
await self.help_command(update, context)
async def get_weather_info(self, city: str) -> dict:
"""天気情報を取得(外部API呼び出し)"""
# 実際の実装では外部天気APIを使用
# ここではサンプルデータを返す
import random
weather_conditions = ['晴れ', '曇り', '雨', '雪']
return {
'name': city.title(),
'weather': random.choice(weather_conditions),
'temperature': random.randint(-10, 35),
'humidity': random.randint(30, 90),
'wind_speed': round(random.uniform(0, 15), 1),
'updated_at': 'Now'
}
async def save_task(self, title: str, user_id: int) -> str:
"""タスクを保存"""
import uuid
task_id = str(uuid.uuid4())[:8]
# 実際の実装ではデータベースに保存
return task_id
async def complete_task(self, task_id: str):
"""タスクを完了にする"""
# 実際の実装ではデータベースを更新
pass
async def extract_text_from_image(self, image_path: str) -> str:
"""画像からテキストを抽出(OCR)"""
# 実際の実装ではOCRライブラリ(Tesseract等)を使用
return "サンプルOCRテキスト"
def run(self):
"""Botを実行"""
logger.info("Bot starting...")
self.application.run_polling(allowed_updates=Update.ALL_TYPES)
# Bot使用例
async def main():
"""メイン関数"""
bot_token = "YOUR_BOT_TOKEN" # BotFatherから取得したトークン
bot = TelegramBot(bot_token)
bot.run()
if __name__ == '__main__':
asyncio.run(main())
Webhook統合(Flask)
from flask import Flask, request, jsonify
import requests
import json
import logging
app = Flask(__name__)
logging.basicConfig(level=logging.INFO)
class TelegramWebhook:
def __init__(self, bot_token: str, webhook_url: str):
self.bot_token = bot_token
self.api_url = f"https://api.telegram.org/bot{bot_token}"
self.webhook_url = webhook_url
def set_webhook(self):
"""Webhookを設定"""
url = f"{self.api_url}/setWebhook"
data = {"url": self.webhook_url}
response = requests.post(url, json=data)
return response.json()
def send_message(self, chat_id: int, text: str, reply_markup=None):
"""メッセージを送信"""
url = f"{self.api_url}/sendMessage"
data = {
"chat_id": chat_id,
"text": text,
"parse_mode": "Markdown"
}
if reply_markup:
data["reply_markup"] = reply_markup
response = requests.post(url, json=data)
return response.json()
def send_photo(self, chat_id: int, photo_url: str, caption: str = ""):
"""画像を送信"""
url = f"{self.api_url}/sendPhoto"
data = {
"chat_id": chat_id,
"photo": photo_url,
"caption": caption
}
response = requests.post(url, json=data)
return response.json()
# グローバルインスタンス
telegram_webhook = TelegramWebhook(
bot_token="YOUR_BOT_TOKEN",
webhook_url="https://yourdomain.com/telegram/webhook"
)
@app.route('/telegram/webhook', methods=['POST'])
def telegram_webhook_handler():
"""Telegramからのwebhookを処理"""
try:
data = request.get_json()
if 'message' in data:
message = data['message']
chat_id = message['chat']['id']
user = message['from']
text = message.get('text', '')
# コマンド処理
if text.startswith('/'):
return handle_command(chat_id, text, user)
# 通常メッセージ処理
return handle_message(chat_id, text, user)
elif 'callback_query' in data:
callback_query = data['callback_query']
return handle_callback_query(callback_query)
return jsonify({"status": "ok"})
except Exception as e:
logging.error(f"Webhook処理エラー: {e}")
return jsonify({"status": "error"}), 500
def handle_command(chat_id: int, command: str, user: dict):
"""コマンドを処理"""
if command == '/start':
welcome_text = f"こんにちは、{user['first_name']}さん!\n\nTelegram Webhook Botへようこそ。"
telegram_webhook.send_message(chat_id, welcome_text)
elif command.startswith('/notify'):
# GitHub通知の例
return handle_github_notification(chat_id, command)
elif command.startswith('/deploy'):
# デプロイメント通知の例
return handle_deployment_command(chat_id, command)
return jsonify({"status": "ok"})
def handle_message(chat_id: int, text: str, user: dict):
"""通常メッセージを処理"""
# キーワード検出
if 'アラート' in text or 'alert' in text.lower():
alert_keyboard = {
"inline_keyboard": [
[{"text": "🚨 緊急", "callback_data": "alert_critical"}],
[{"text": "⚠️ 警告", "callback_data": "alert_warning"}],
[{"text": "ℹ️ 情報", "callback_data": "alert_info"}]
]
}
telegram_webhook.send_message(
chat_id,
"アラートレベルを選択してください:",
reply_markup=alert_keyboard
)
else:
telegram_webhook.send_message(chat_id, f"メッセージを受信しました: {text}")
return jsonify({"status": "ok"})
def handle_callback_query(callback_query: dict):
"""コールバッククエリを処理"""
query_id = callback_query['id']
chat_id = callback_query['message']['chat']['id']
data = callback_query['data']
# アラート処理
if data.startswith('alert_'):
level = data.replace('alert_', '')
handle_alert(chat_id, level)
# コールバッククエリに応答
answer_url = f"{telegram_webhook.api_url}/answerCallbackQuery"
requests.post(answer_url, json={"callback_query_id": query_id})
return jsonify({"status": "ok"})
def handle_github_notification(chat_id: int, command: str):
"""GitHub通知を処理"""
# 実際のGitHub webhook統合例
github_data = {
"repository": "example/repo",
"commits": 3,
"pusher": "developer",
"branch": "main"
}
notification_text = f"""
🚀 **GitHub Push Notification**
Repository: {github_data['repository']}
Branch: {github_data['branch']}
Commits: {github_data['commits']}
Pusher: {github_data['pusher']}
"""
telegram_webhook.send_message(chat_id, notification_text)
return jsonify({"status": "ok"})
def handle_deployment_command(chat_id: int, command: str):
"""デプロイメントコマンドを処理"""
deployment_keyboard = {
"inline_keyboard": [
[{"text": "🔧 Staging", "callback_data": "deploy_staging"}],
[{"text": "🚀 Production", "callback_data": "deploy_production"}]
]
}
telegram_webhook.send_message(
chat_id,
"デプロイ環境を選択してください:",
reply_markup=deployment_keyboard
)
return jsonify({"status": "ok"})
def handle_alert(chat_id: int, level: str):
"""アラートを処理"""
emojis = {
"critical": "🚨",
"warning": "⚠️",
"info": "ℹ️"
}
alert_text = f"{emojis[level]} **{level.upper()} Alert**\n\nアラートが発生しました。詳細を確認してください。"
telegram_webhook.send_message(chat_id, alert_text)
# 外部システム統合用エンドポイント
@app.route('/api/send-notification', methods=['POST'])
def send_notification():
"""外部システムからの通知送信"""
try:
data = request.get_json()
chat_id = data.get('chat_id')
message = data.get('message')
if chat_id and message:
telegram_webhook.send_message(chat_id, message)
return jsonify({"status": "sent"})
return jsonify({"status": "error", "message": "chat_id and message required"}), 400
except Exception as e:
logging.error(f"通知送信エラー: {e}")
return jsonify({"status": "error"}), 500
if __name__ == '__main__':
# Webhookを設定
webhook_result = telegram_webhook.set_webhook()
print(f"Webhook設定結果: {webhook_result}")
# Flaskアプリを実行
app.run(debug=True, port=5000)
ミニアプリ開発(JavaScript)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Telegram Mini App</title>
<script src="https://telegram.org/js/telegram-web-app.js"></script>
<style>
body {
margin: 0;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background-color: var(--tg-theme-bg-color);
color: var(--tg-theme-text-color);
}
.container {
max-width: 400px;
margin: 0 auto;
}
.card {
background: var(--tg-theme-secondary-bg-color);
border-radius: 12px;
padding: 16px;
margin-bottom: 16px;
}
.button {
background: var(--tg-theme-button-color);
color: var(--tg-theme-button-text-color);
border: none;
border-radius: 8px;
padding: 12px 24px;
font-size: 16px;
cursor: pointer;
width: 100%;
margin-bottom: 8px;
}
.input {
width: 100%;
padding: 12px;
border: 1px solid var(--tg-theme-hint-color);
border-radius: 8px;
background: var(--tg-theme-bg-color);
color: var(--tg-theme-text-color);
font-size: 16px;
margin-bottom: 16px;
}
</style>
</head>
<body>
<div class="container">
<div class="card">
<h2>🎯 タスク管理ミニアプリ</h2>
<input type="text" id="taskInput" class="input" placeholder="新しいタスクを入力...">
<button class="button" onclick="addTask()">➕ タスク追加</button>
</div>
<div class="card">
<h3>📋 タスクリスト</h3>
<div id="taskList"></div>
</div>
<div class="card">
<h3>🎮 ゲーム機能</h3>
<button class="button" onclick="startGame()">🎲 ゲーム開始</button>
<button class="button" onclick="shareScore()">📊 スコア共有</button>
</div>
<div class="card">
<h3>💰 支払い機能</h3>
<button class="button" onclick="makePurchase()">🛒 購入する</button>
</div>
</div>
<script>
// Telegram Web App初期化
const tg = window.Telegram.WebApp;
// Web App設定
tg.ready();
tg.expand();
// ユーザー情報の取得
const user = tg.initDataUnsafe?.user;
if (user) {
console.log('User:', user);
}
// タスクデータ
let tasks = [];
// タスク追加機能
function addTask() {
const taskInput = document.getElementById('taskInput');
const taskText = taskInput.value.trim();
if (taskText) {
const task = {
id: Date.now(),
text: taskText,
completed: false
};
tasks.push(task);
taskInput.value = '';
renderTasks();
// Telegram Botにデータを送信
sendDataToBot('task_created', task);
// ハプティックフィードバック
tg.HapticFeedback.impactOccurred('medium');
// 成功メッセージ
tg.showAlert('タスクが追加されました!');
}
}
// タスクリスト表示
function renderTasks() {
const taskList = document.getElementById('taskList');
if (tasks.length === 0) {
taskList.innerHTML = '<p>タスクがありません</p>';
return;
}
taskList.innerHTML = tasks.map(task => `
<div style="display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid var(--tg-theme-hint-color);">
<span style="${task.completed ? 'text-decoration: line-through; opacity: 0.6;' : ''}">${task.text}</span>
<div>
<button onclick="toggleTask(${task.id})" style="background: none; border: none; font-size: 20px; cursor: pointer;">
${task.completed ? '✅' : '⭕'}
</button>
<button onclick="deleteTask(${task.id})" style="background: none; border: none; font-size: 16px; cursor: pointer; color: #ff4444;">
🗑️
</button>
</div>
</div>
`).join('');
}
// タスク状態切り替え
function toggleTask(taskId) {
const task = tasks.find(t => t.id === taskId);
if (task) {
task.completed = !task.completed;
renderTasks();
sendDataToBot('task_updated', task);
tg.HapticFeedback.impactOccurred('light');
}
}
// タスク削除
function deleteTask(taskId) {
tg.showConfirm('タスクを削除しますか?', (confirmed) => {
if (confirmed) {
tasks = tasks.filter(t => t.id !== taskId);
renderTasks();
sendDataToBot('task_deleted', { id: taskId });
tg.HapticFeedback.impactOccurred('heavy');
}
});
}
// ゲーム機能
function startGame() {
// シンプルなクリッカーゲーム
let score = 0;
let gameTime = 10;
const gameContainer = document.createElement('div');
gameContainer.innerHTML = `
<div style="text-align: center; padding: 20px;">
<h3>🎯 クリッカーゲーム</h3>
<div style="font-size: 24px; margin: 20px 0;">時間: <span id="gameTimer">${gameTime}</span>秒</div>
<div style="font-size: 32px; margin: 20px 0;">スコア: <span id="gameScore">${score}</span></div>
<button id="clickButton" style="font-size: 48px; padding: 20px; border: none; border-radius: 50%; background: var(--tg-theme-button-color);">🎯</button>
</div>
`;
// 既存のコンテンツを置き換え
document.querySelector('.container').innerHTML = '';
document.querySelector('.container').appendChild(gameContainer);
const timer = setInterval(() => {
gameTime--;
document.getElementById('gameTimer').textContent = gameTime;
if (gameTime <= 0) {
clearInterval(timer);
endGame(score);
}
}, 1000);
document.getElementById('clickButton').onclick = () => {
score++;
document.getElementById('gameScore').textContent = score;
tg.HapticFeedback.impactOccurred('light');
};
}
// ゲーム終了
function endGame(finalScore) {
tg.showAlert(`ゲーム終了!\\nスコア: ${finalScore}点`, () => {
location.reload(); // アプリを再読み込み
});
// スコアをBotに送信
sendDataToBot('game_finished', { score: finalScore });
}
// スコア共有
function shareScore() {
const totalTasks = tasks.length;
const completedTasks = tasks.filter(t => t.completed).length;
const shareText = `🎯 私のタスク進捗\\n✅ 完了: ${completedTasks}/${totalTasks}\\n📊 達成率: ${totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0}%`;
// テキスト共有
tg.shareText(shareText);
}
// 購入機能
function makePurchase() {
// Telegram Payments API使用
const invoice = {
title: 'プレミアム機能',
description: 'タスク管理の高度な機能を解除します',
payload: 'premium_upgrade',
provider_token: 'YOUR_PAYMENT_PROVIDER_TOKEN',
currency: 'JPY',
prices: [{ label: 'プレミアム機能', amount: 50000 }] // 500円(金額は最小単位)
};
tg.invoiceCreate(invoice, (result) => {
if (result.success) {
tg.showAlert('購入が完了しました!');
sendDataToBot('purchase_completed', result);
} else {
tg.showAlert('購入がキャンセルされました。');
}
});
}
// Botにデータ送信
function sendDataToBot(action, data) {
tg.sendData(JSON.stringify({
action: action,
data: data,
user: user,
timestamp: Date.now()
}));
}
// Web App終了処理
function closeApp() {
tg.close();
}
// MainButtonの設定
tg.MainButton.setText('完了');
tg.MainButton.onClick(() => {
sendDataToBot('app_completed', { tasks: tasks });
tg.close();
});
tg.MainButton.show();
// BackButtonの設定
tg.BackButton.onClick(() => {
tg.showConfirm('アプリを終了しますか?', (confirmed) => {
if (confirmed) {
tg.close();
}
});
});
tg.BackButton.show();
// 初期レンダリング
renderTasks();
// デバイス情報の取得
console.log('Platform:', tg.platform);
console.log('Version:', tg.version);
console.log('ViewportHeight:', tg.viewportHeight);
console.log('ViewportStableHeight:', tg.viewportStableHeight);
</script>
</body>
</html>
環境変数設定
# .env ファイル
# Telegram Bot設定
TELEGRAM_BOT_TOKEN=your-bot-token-from-botfather
TELEGRAM_WEBHOOK_URL=https://yourdomain.com/telegram/webhook
# 外部API連携
OPENWEATHER_API_KEY=your-openweather-api-key
GOOGLE_TRANSLATE_API_KEY=your-google-translate-key
# データベース設定
DATABASE_URL=postgresql://user:password@localhost/telegram_bot
REDIS_URL=redis://localhost:6379
# ファイルストレージ
AWS_S3_BUCKET=your-s3-bucket
AWS_ACCESS_KEY_ID=your-aws-access-key
AWS_SECRET_ACCESS_KEY=your-aws-secret-key
# 支払い処理
STRIPE_SECRET_KEY=your-stripe-secret-key
TELEGRAM_PAYMENT_PROVIDER_TOKEN=your-payment-provider-token
# ミニアプリ設定
MINI_APP_URL=https://your-mini-app.com
MINI_APP_SECRET=your-mini-app-secret
Docker設定例
# Dockerfile
FROM python:3.11-slim
WORKDIR /app
# システム依存関係のインストール
RUN apt-get update && apt-get install -y \
tesseract-ocr \
tesseract-ocr-jpn \
libgl1-mesa-glx \
libglib2.0-0 \
&& rm -rf /var/lib/apt/lists/*
# Python依存関係のインストール
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# アプリケーションファイルのコピー
COPY . .
# 環境変数
ENV PYTHONPATH=/app
ENV PYTHONUNBUFFERED=1
# ポート公開
EXPOSE 5000
# ヘルスチェック
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:5000/health || exit 1
# アプリケーション起動
CMD ["python", "bot.py"]
Docker Compose設定
# docker-compose.yml
version: '3.8'
services:
telegram-bot:
build: .
container_name: telegram-bot
restart: unless-stopped
environment:
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
- DATABASE_URL=postgresql://postgres:password@postgres:5432/telegram_bot
- REDIS_URL=redis://redis:6379
depends_on:
- postgres
- redis
volumes:
- ./downloads:/app/downloads
ports:
- "5000:5000"
postgres:
image: postgres:15-alpine
container_name: postgres
restart: unless-stopped
environment:
- POSTGRES_DB=telegram_bot
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:7-alpine
container_name: redis
restart: unless-stopped
volumes:
- redis_data:/data
ports:
- "6379:6379"
volumes:
postgres_data:
redis_data:
CI/CD通知連携例
# ci_cd_integration.py - CI/CDパイプライン通知
import requests
import json
class TelegramCINotifier:
def __init__(self, bot_token: str, chat_id: str):
self.bot_token = bot_token
self.chat_id = chat_id
self.api_url = f"https://api.telegram.org/bot{bot_token}"
def send_build_notification(self, status: str, project: str, branch: str, commit: str):
"""ビルド通知を送信"""
emoji = "✅" if status == "success" else "❌"
message = f"""
{emoji} **ビルド {status.upper()}**
プロジェクト: {project}
ブランチ: {branch}
コミット: {commit[:8]}
時刻: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
"""
keyboard = {
"inline_keyboard": [
[{"text": "📊 詳細を見る", "url": f"https://ci.example.com/builds/{commit}"}],
[{"text": "🚀 デプロイ", "callback_data": f"deploy_{project}_{branch}"}]
]
} if status == "success" else None
self.send_message(message, keyboard)
def send_deployment_notification(self, environment: str, status: str, version: str):
"""デプロイ通知を送信"""
emoji = "🚀" if status == "success" else "💥"
message = f"""
{emoji} **デプロイ {status.upper()}**
環境: {environment}
バージョン: {version}
時刻: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
"""
self.send_message(message)
def send_message(self, text: str, reply_markup=None):
"""メッセージ送信"""
url = f"{self.api_url}/sendMessage"
data = {
"chat_id": self.chat_id,
"text": text,
"parse_mode": "Markdown"
}
if reply_markup:
data["reply_markup"] = reply_markup
requests.post(url, json=data)