<?php
date_default_timezone_set('Asia/Tehran');
require_once __DIR__.'/../config.php';
require_once __DIR__.'/../botapi.php';
require_once __DIR__.'/../function.php';
@ignore_user_abort(true);
@set_time_limit(0);
$base=__DIR__;
$infoPath=$base.'/info';
$usersPath=$base.'/users.json';
$statsPath=$base.'/stats.json';
$lockPath=$base.'/broadcast.lock';
$lock=@fopen($lockPath,'c');
if(!$lock||!@flock($lock,LOCK_EX|LOCK_NB)){return;}
if(!is_file($infoPath)||!is_file($usersPath)){return;}
$info=json_decode(@file_get_contents($infoPath),true);
if(!is_array($info)){$info=[];}
$usersRaw=@file_get_contents($usersPath);
$usersArr=json_decode($usersRaw,true);
if(!is_array($usersArr)){$usersArr=[];}
$userIds=[];
foreach($usersArr as $u){
if(is_array($u)&&isset($u['id'])){$userIds[]=$u['id'];}
elseif(is_object($u)&&isset($u->id)){$userIds[]=$u->id;}
elseif(is_numeric($u)||is_string($u)){$userIds[]=$u;}
}
$userIds=array_values(array_filter($userIds,function($v){return $v!==null&&$v!=='';}));
$readStats=function()use($statsPath,$userIds,$info){
$s=@file_get_contents($statsPath);
$j=json_decode($s,true);
if(!is_array($j)){
$j=['started_at'=>time(),'total'=>count($userIds),'processed'=>0,'ok'=>0,'failed'=>0,'blocked'=>0,'errors'=>0,'deleted'=>0,'type'=>isset($info['type'])?$info['type']:'','btnmessage'=>isset($info['btnmessage'])?$info['btnmessage']:'none','pingmessage'=>isset($info['pingmessage'])?$info['pingmessage']:'no'];
@file_put_contents($statsPath,json_encode($j,JSON_UNESCAPED_UNICODE));
}
if(!isset($j['total'])||$j['total']<count($userIds)){$j['total']=count($userIds)+($j['processed']??0);}
return $j;
};
$writeStats=function($j)use($statsPath){
@file_put_contents($statsPath,json_encode($j,JSON_UNESCAPED_UNICODE));
};
$stats=$readStats();
$datatextbot=['text_usertest'=>'','text_support'=>'','text_help'=>'','text_sell'=>'','text_affiliates'=>'','text_Add_Balance'=>''];
$datatextbotget=select("textbot","*",null,null,"fetchAll");
if(is_array($datatextbotget)){
foreach($datatextbotget as $row){
if(isset($row['id_text'])&&array_key_exists($row['id_text'],$datatextbot)){$datatextbot[$row['id_text']]=$row['text']??'';}
}
}
$cancelmessage=json_encode(['inline_keyboard'=>[[['text'=>"لغو عملیات",'callback_data'=>'cancel_sendmessage']]]],JSON_UNESCAPED_UNICODE);
$btn=$info['btnmessage']??'none';
$replyMarkup=null;
if($btn==='buy'){$replyMarkup=json_encode(['inline_keyboard'=>[[['text'=>$datatextbot['text_sell'],'callback_data'=>'buy']]]],JSON_UNESCAPED_UNICODE);}
elseif($btn==='start'){$replyMarkup=json_encode(['inline_keyboard'=>[[['text'=>"شروع",'callback_data'=>'start']]]],JSON_UNESCAPED_UNICODE);}
elseif($btn==='usertestbtn'){$replyMarkup=json_encode(['inline_keyboard'=>[[['text'=>$datatextbot['text_usertest'],'callback_data'=>'usertestbtn']]]],JSON_UNESCAPED_UNICODE);}
elseif($btn==='helpbtn'){$replyMarkup=json_encode(['inline_keyboard'=>[[['text'=>$datatextbot['text_help'],'callback_data'=>'helpbtn']]]],JSON_UNESCAPED_UNICODE);}
elseif($btn==='affiliatesbtn'){$replyMarkup=json_encode(['inline_keyboard'=>[[['text'=>$datatextbot['text_affiliates'],'callback_data'=>'affiliatesbtn']]]],JSON_UNESCAPED_UNICODE);}
elseif($btn==='addbalance'){$replyMarkup=json_encode(['inline_keyboard'=>[[['text'=>$datatextbot['text_Add_Balance'],'callback_data'=>'Add_Balance']]]],JSON_UNESCAPED_UNICODE);}
if(count($userIds)===0){
if(isset($info['id_admin'],$info['id_message'])){
$ended=time();
$duration=max(0,$ended-($stats['started_at']??$ended));
$total=(int)($stats['total']??0);
$ok=(int)($stats['ok']??0);
$failed=(int)($stats['failed']??0);
$blocked=(int)($stats['blocked']??0);
$errors=(int)($stats['errors']??0);
$deleted=(int)($stats['deleted']??0);
$notBlocked=max(0,$total-$blocked);
$done=max(0,($stats['processed']??0));
$report="✅ عملیات ارسال همگانی پایان یافت.\n\n📊 گزارش نهایی:\n👥 کل کاربران: ".$total."\n✅ ارسال موفق: ".$ok."\n❌ ارسال ناموفق: ".$failed."\n⛔️ بلاک کرده‌اند: ".$blocked."\n✅ بلاک نکرده‌اند: ".$notBlocked."\n⚠️ خطاها (غیراز بلاک): ".$errors."\n🧹 حذف‌شده‌ها (بدون فاکتور و موجودی): ".$deleted."\n⏱ مدت زمان: ".$duration." ثانیه\n🧾 پردازش‌شده: ".$done."\n🕒 زمان پایان: ".date('Y/m/d H:i:s',$ended);
@deletemessage($info['id_admin'],$info['id_message']);
@sendmessage($info['id_admin'],$report,null,'HTML');
}
@unlink($infoPath);
@unlink($usersPath);
@unlink($statsPath);
return;
}
$maxExec=(int)@ini_get('max_execution_time');
if($maxExec<=0){$maxExec=30;}
$softTimeLimit=max(10,min(55,$maxExec-4));
$batchSize=300;
$startTime=microtime(true);
$processedNow=0;
$sentNow=0;
$failedNow=0;
$blockedNow=0;
$errorsNow=0;
$deletedNow=0;
$type=$info['type']??'sendmessage';
$ping=($info['pingmessage']??'no')==='yes';
$messageText=$info['message']??'';
$take=min($batchSize,count($userIds));
$batch=array_slice($userIds,0,$take);
$remain=array_slice($userIds,$take);
foreach($batch as $uid){
if((microtime(true)-$startTime)>=$softTimeLimit){$remain=array_merge([$uid],array_slice($batch,$processedNow+1),$remain);break;}
$processedNow++;
$stats['processed']=($stats['processed']??0)+1;
$resp=null;
if($type==='unpinmessage'){
$resp=@unpinmessage($uid);
$sentNow++;
$stats['ok']=($stats['ok']??0)+1;
continue;
}
if($type==='forwardmessage'){
$attempt=0;
while(true){
$resp=@forwardMessage($info['id_admin']??null,$messageText,$uid);
if(is_array($resp)&&isset($resp['ok'])&&$resp['ok']){break;}
if(is_array($resp)&&isset($resp['error_code'])&&$resp['error_code']==429&&isset($resp['parameters']['retry_after'])){@sleep((int)$resp['parameters']['retry_after']);$attempt++;if($attempt<2){continue;}}
if(is_array($resp)&&isset($resp['error_code'])&&in_array((int)$resp['error_code'],[500,502,503,504],true)){@usleep(500000);$attempt++;if($attempt<2){continue;}}
break;
}
if(is_array($resp)&&isset($resp['ok'])&&$resp['ok']){
$sentNow++;
$stats['ok']=($stats['ok']??0)+1;
if($ping&&isset($resp['result']['message_id'])){@pinmessage($uid,$resp['result']['message_id']);}
}else{
$failedNow++;
$stats['failed']=($stats['failed']??0)+1;
$desc=is_array($resp)&&isset($resp['description'])?$resp['description']:'';
if(stripos($desc,'Forbidden: bot was blocked by the user')!==false){
$blockedNow++;
$stats['blocked']=($stats['blocked']??0)+1;
$invoicecount=select("invoice","*","id_user",$uid,"count");
$userinfo=select("user","Balance","id",$uid,"select");
if((int)$invoicecount===0&&isset($userinfo['Balance'])&&(float)$userinfo['Balance']==0.0){
try{$stmt=$pdo->prepare("DELETE FROM user WHERE id=:id");$stmt->execute([':id'=>$uid]);$deletedNow++;$stats['deleted']=($stats['deleted']??0)+1;}catch(Exception $e){}
}
}else{$errorsNow++;$stats['errors']=($stats['errors']??0)+1;}
}
continue;
}
$attempt=0;
while(true){
if($btn==='none'){$resp=@sendmessage($uid,$messageText,null,'HTML');}
else{$resp=@sendmessage($uid,$messageText,$replyMarkup,'HTML');}
if(is_array($resp)&&isset($resp['ok'])&&$resp['ok']){break;}
if(is_array($resp)&&isset($resp['error_code'])&&$resp['error_code']==429&&isset($resp['parameters']['retry_after'])){@sleep((int)$resp['parameters']['retry_after']);$attempt++;if($attempt<2){continue;}}
if(is_array($resp)&&isset($resp['error_code'])&&in_array((int)$resp['error_code'],[500,502,503,504],true)){@usleep(500000);$attempt++;if($attempt<2){continue;}}
break;
}
if(is_array($resp)&&isset($resp['ok'])&&$resp['ok']){
$sentNow++;
$stats['ok']=($stats['ok']??0)+1;
if($ping&&isset($resp['result']['message_id'])){@pinmessage($uid,$resp['result']['message_id']);}
}else{
$failedNow++;
$stats['failed']=($stats['failed']??0)+1;
$desc=is_array($resp)&&isset($resp['description'])?$resp['description']:'';
if(stripos($desc,'Forbidden: bot was blocked by the user')!==false){
$blockedNow++;
$stats['blocked']=($stats['blocked']??0)+1;
$invoicecount=select("invoice","*","id_user",$uid,"count");
$userinfo=select("user","Balance","id",$uid,"select");
if((int)$invoicecount===0&&isset($userinfo['Balance'])&&(float)$userinfo['Balance']==0.0){
try{$stmt=$pdo->prepare("DELETE FROM user WHERE id=:id");$stmt->execute([':id'=>$uid]);$deletedNow++;$stats['deleted']=($stats['deleted']??0)+1;}catch(Exception $e){}
}
}else{$errorsNow++;$stats['errors']=($stats['errors']??0)+1;}
}
}
$writeStats($stats);
$remainOut=[];
foreach($remain as $id){$remainOut[]=['id'=>$id];}
@file_put_contents($usersPath,json_encode($remainOut,JSON_UNESCAPED_UNICODE));
$remainCount=count($remain);
$total=(int)($stats['total']??0);
$done=(int)($stats['processed']??0);
$ok=(int)($stats['ok']??0);
$failed=(int)($stats['failed']??0);
$blocked=(int)($stats['blocked']??0);
$errors=(int)($stats['errors']??0);
$deleted=(int)($stats['deleted']??0);
$progress="✏️ عملیات ارسال همگانی در حال انجام است...\n\n👥 کل: ".$total."\n🧾 پردازش‌شده: ".$done."\n⏳ باقی‌مانده: ".$remainCount."\n\n✅ موفق تا الان: ".$ok."\n❌ ناموفق تا الان: ".$failed."\n⛔️ بلاک تا الان: ".$blocked."\n⚠️ خطا تا الان: ".$errors."\n🧹 حذف‌شده‌ها: ".$deleted."\n\n🚀 این اجرا:\n🧾 پردازش: ".$processedNow."\n✅ موفق: ".$sentNow."\n❌ ناموفق: ".$failedNow."\n⛔️ بلاک: ".$blockedNow."\n⚠️ خطا: ".$errorsNow;
if(isset($info['id_admin'],$info['id_message'])){@Editmessagetext($info['id_admin'],$info['id_message'],$progress,$cancelmessage);}
