أتمتة بطاقة اللكم باستخدام بوت موقع تلغرام | أصلي، ترجم بواسطة AI
هل سبق لك أن تمنيت أن تكون بطاقة الحضور اليومية أقل إرهاقًا؟ بالتأكيد حدث ذلك معي. لهذا السبب قمت ببناء بوت شخصي على Telegram يستخدم تتبع الموقع لأتمتة إشعارات الوصول إلى المكتب وتذكيري بتلك التسجيلات الحاسمة. يناقش هذا المنشور كيف جمعت بين Python وGitHub Actions لإنشاء نظام سلس وخالٍ من المتاعب، يبقيني على اطلاع عندما أحتاج إليه، كل ذلك بناءً على موقعي.
name: Hourly Location Check
on:
schedule:
# Run every hour, on the hour, between 11 AM and 11 PM, on weekdays (Monday-Friday)
# The time is in UTC. Singapore time (SGT) is UTC+8.
# So, 11 AM SGT is 03:00 UTC, and 11 PM SGT is 15:00 UTC.
# Therefore, we need to schedule from 03:00 to 15:00 UTC.
- cron: '0 3-15 * * 1-5'
# Reminder to START sharing live location: Wednesday 11 AM SGT (3 AM UTC)
# Current time: Sunday, June 8, 2025 at 5:10:58 PM +08 (SGT)
# For Wednesday 11 AM SGT (UTC+8): 11 - 8 = 3 AM UTC.
- cron: '0 3 * * 3' # 3 for Wednesday
# Reminder to STOP sharing live location: Friday 11 PM SGT (3 PM UTC)
# Current time: Sunday, June 8, 2025 at 5:10:58 PM +08 (SGT)
# For Friday 11 PM SGT (UTC+8): 23 - 8 = 15 PM UTC.
- cron: '0 15 * * 5' # 5 for Friday
workflow_dispatch: # Allows manual triggering of the workflow
push:
branches: ["main"]
paths:
- 'scripts/release/location_bot.py' # Corrected path to your script
- '.github/workflows/location.yml' # Path to this workflow file
concurrency:
group: 'location'
cancel-in-progress: false
jobs:
check_and_notify:
runs-on: ubuntu-latest
env:
TELEGRAM_LOCATION_BOT_API_KEY: $
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 5 # Fetch only the last 5 commits for efficiency
- name: Set up Python 3.13.2
uses: actions/setup-python@v4
with:
python-version: "3.13.2" # Specify the exact Python version
- name: Install dependencies
run: |
python -m pip install --upgrade pip
# Assuming you have a requirements.simple.txt in your repo root.
# If not, use: pip install requests python-dotenv
pip install -r requirements.simple.txt
- name: Run location check script (Scheduled)
run: python scripts/release/location_bot.py --job check_location
# This step will run on scheduled triggers for the hourly check
if: github.event.schedule == '0 3-15 * * 1-5' # Match the hourly cron schedule
- name: Reminder to START sharing live location
run: python scripts/release/location_bot.py --job start_sharing_message
if: github.event.schedule == '0 3 * * 3' # Matches Wednesday 11 AM SGT cron
- name: Reminder to STOP sharing live location
run: python scripts/release/location_bot.py --job stop_sharing_message
if: github.event.schedule == '0 15 * * 5' # Matches Friday 11 PM SGT cron
- name: Run Telegram script for test message (Manual Trigger)
run: python scripts/release/location_bot.py --job send_message --message "This is a manual trigger test message from GitHub Actions."
if: github.event_name == 'workflow_dispatch'
- name: Run Telegram script for push to main branch
run: python scripts/release/location_bot.py --job send_message --message "Code changes for location bot pushed to main branch."
if: github.event_name == 'push'
import os
import requests
from dotenv import load_dotenv
import json
import subprocess
import argparse
import math
import time # For potential future continuous monitoring
load_dotenv()
# New: Specific API key for the location bot
TELEGRAM_LOCATION_BOT_API_KEY = os.environ.get("TELEGRAM_LOCATION_BOT_API_KEY") # Ensure this is set in your .env
TELEGRAM_CHAT_ID = "610574272" # This chat ID is for sending the notification message
# Define your office coordinates
OFFICE_LATITUDE = 23.135368
OFFICE_LONGITUDE = 113.32952
# Proximity radius in meters
PROXIMITY_RADIUS_METERS = 300
def send_telegram_message(bot_token, chat_id, message):
"""Sends a message to a Telegram chat using the Telegram Bot API."""
url = f"https://api.telegram.org/bot{bot_token}/sendMessage"
params = {
"chat_id": chat_id,
"text": message,
"parse_mode": "Markdown" # Using Markdown for bold text in the message
}
response = requests.post(url, params=params)
if response.status_code != 200:
print(f"Error sending Telegram message: {response.status_code} - {response.text}")
def get_latest_location(bot_token):
"""Retrieves the latest live location update from the bot."""
url = f"https://api.telegram.org/bot{bot_token}/getUpdates"
# Offset to get only new updates after the last processed one (for continuous polling)
# For a simple run-once script, we'll just get the latest, but for polling, you'd manage an offset.
params = {"offset": -1} # Get the very last update
response = requests.get(url, params=params)
print("GetUpdates Response:", response) # Debugging
if response.status_code == 200:
updates = response.json()
print("GetUpdates JSON:", json.dumps(updates, indent=4)) # Debugging
if updates['result']:
last_update = updates['result'][-1]
# Prioritize edited_message for live locations
if 'edited_message' in last_update and 'location' in last_update['edited_message']:
return last_update['edited_message']['location'], last_update['edited_message']['chat']['id']
elif 'message' in last_update and 'location' in last_update['message']:
# Handle initial live location messages or static location shares
return last_update['message']['location'], last_update['message']['chat']['id']
return None, None
def haversine_distance(lat1, lon1, lat2, lon2):
"""
Calculate the distance between two points on Earth using the Haversine formula.
Returns distance in meters.
"""
R = 6371000 # Radius of Earth in meters
lat1_rad = math.radians(lat1)
lon1_rad = math.radians(lon1)
lat2_rad = math.radians(lat2)
lon2_rad = math.radians(lon2)
dlon = lon2_rad - lon1_rad
dlat = lat2_rad - lat1_rad
a = math.sin(dlat / 2)**2 + math.cos(lat1_rad) * math.cos(lat2_rad) * math.sin(dlon / 2)**2
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
distance = R * c
return distance
def main():
parser = argparse.ArgumentParser(description="Telegram Bot Script")
# Updated choices for --job argument
parser.add_argument('--job', choices=['get_chat_id', 'send_message', 'check_location', 'start_sharing_message', 'stop_sharing_message'], required=True, help="Job to perform")
# Added --message argument for 'send_message' job
parser.add_argument('--message', type=str, help="Message to send for 'send_message' job")
# Added --test argument for 'check_location' job
parser.add_argument('--test', action='store_true', help="For 'check_location' job, force sending a message regardless of proximity.")
args = parser.parse_args()
if args.job == 'get_chat_id':
bot_token = TELEGRAM_LOCATION_BOT_API_KEY
url = f"https://api.telegram.org/bot{bot_token}/getUpdates"
response = requests.get(url)
if response.status_code == 200:
updates = response.json()
print(json.dumps(updates, indent=4))
if updates['result']:
last_update = updates['result'][-1]
chat_id = None
if 'message' in last_update and 'chat' in last_update['message']:
chat_id = last_update['message']['chat']['id']
elif 'edited_message' in last_update and 'chat' in last_update['edited_message']:
chat_id = last_update['edited_message']['chat']['id']
elif 'channel_post' in last_update and 'chat' in last_update['channel_post']:
chat_id = last_update['channel_post']['chat']['id']
elif 'edited_channel_post' in last_update and 'chat' in last_update['edited_channel_post']:
chat_id = last_update['edited_channel_post']['chat']['id']
if chat_id:
print(f"Chat ID: {chat_id}")
else:
print("Could not retrieve chat ID from the last update.")
else:
print("No updates found.")
else:
print(f"Error fetching updates: {response.status_code} - {response.text}")
elif args.job == 'send_message':
if TELEGRAM_LOCATION_BOT_API_KEY and TELEGRAM_CHAT_ID:
message = args.message if args.message else "This is a default test message from your Telegram bot script!"
send_telegram_message(TELEGRAM_LOCATION_BOT_API_KEY, TELEGRAM_CHAT_ID, message)
print(f"Message sent successfully: {message}")
else:
print("TELEGRAM_LOCATION_BOT_API_KEY and TELEGRAM_CHAT_ID are not set.")
elif args.job == 'start_sharing_message':
if TELEGRAM_LOCATION_BOT_API_KEY and TELEGRAM_CHAT_ID:
message = "⚠️ *Reminder:* Please start sharing your live location to the bot!"
send_telegram_message(TELEGRAM_LOCATION_BOT_API_KEY, TELEGRAM_CHAT_ID, message)
print("Start sharing reminder sent.")
else:
print("TELEGRAM_LOCATION_BOT_API_KEY and TELEGRAM_CHAT_ID are not set.")
elif args.job == 'stop_sharing_message':
if TELEGRAM_LOCATION_BOT_API_KEY and TELEGRAM_CHAT_ID:
message = "✅ *Reminder:* You can stop sharing your live location now."
send_telegram_message(TELEGRAM_LOCATION_BOT_API_KEY, TELEGRAM_CHAT_ID, message)
print("Stop sharing reminder sent.")
else:
print("TELEGRAM_LOCATION_BOT_API_KEY and TELEGRAM_CHAT_ID are not set.")
elif args.job == 'check_location':
if not TELEGRAM_LOCATION_BOT_API_KEY or not TELEGRAM_CHAT_ID:
print("TELEGRAM_LOCATION_BOT_API_KEY and TELEGRAM_CHAT_ID must be set for location checks.")
return
user_location, location_chat_id = get_latest_location(TELEGRAM_LOCATION_BOT_API_KEY)
if user_location:
current_latitude = user_location['latitude']
current_longitude = user_location['longitude']
distance = haversine_distance(
OFFICE_LATITUDE, OFFICE_LONGITUDE,
current_latitude, current_longitude
)
print(f"Current location: ({current_latitude}, {current_longitude})")
print(f"Distance to office: {distance:.2f} meters")
needs_punch_card = distance <= PROXIMITY_RADIUS_METERS
if needs_punch_card:
print(f"You are within {PROXIMITY_RADIUS_METERS}m of the office!")
notification_message = (
f"🎉 *Arrived Office!* 🎉\n"
f"Time to Punch card in WeCom.\n"
f"Your current distance from office: {distance:.2f}m."
)
else:
print(f"You are outside the {PROXIMITY_RADIUS_METERS}m office circle.")
# Message for when outside the radius
notification_message = (
f"📍 You are *outside* the office proximity ({PROXIMITY_RADIUS_METERS}m).\n"
f"No punch card needed at this time.\n"
f"Your current distance from office: {distance:.2f}m."
)
# Send message if within proximity OR if --test flag is used
if needs_punch_card or args.test:
send_telegram_message(TELEGRAM_LOCATION_BOT_API_KEY, TELEGRAM_CHAT_ID, notification_message)
else:
# If not within proximity AND not in test mode, just print to console (no Telegram message)
print("Not within proximity and not in test mode, no message sent to Telegram.")
else:
print("Could not retrieve your latest location. Make sure you are sharing live location with the bot.")
if __name__ == '__main__':
main()