FacebookService.php 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. <?php
  2. namespace App\Services\Smm;
  3. use App\Services\Contracts\SmmPlatformInterface;
  4. use Carbon\Carbon;
  5. use Illuminate\Http\Request;
  6. use Illuminate\Support\Facades\Http;
  7. use Illuminate\Support\Facades\Log;
  8. use Illuminate\Support\Facades\Session;
  9. class FacebookService implements SmmPlatformInterface
  10. {
  11. protected $configData;
  12. protected $graphVersion = 'v19.0';
  13. public function __construct($configData)
  14. {
  15. $this->configData = $configData;
  16. }
  17. public function login()
  18. {
  19. $permissions = [
  20. 'publish_video',
  21. 'pages_manage_cta',
  22. 'pages_show_list',
  23. 'business_management',
  24. 'pages_read_engagement',
  25. 'pages_manage_metadata',
  26. 'pages_read_user_content',
  27. 'pages_manage_ads',
  28. 'pages_manage_posts',
  29. 'pages_manage_engagement',
  30. ];
  31. $query = http_build_query([
  32. 'client_id' => env('SSM_FACEBOOK_APP_ID'),
  33. 'redirect_uri' => env('DIST_SITE_URL').'/dist/callback/facebook',
  34. 'response_type' => 'code',
  35. 'scope' => implode(',', $permissions),
  36. 'state' => csrf_token(),
  37. ]);
  38. return ['status' => true, 'data' => ['url' => "https://www.facebook.com/{$this->graphVersion}/dialog/oauth?{$query}"]];
  39. }
  40. public function loginCallback(Request $request)
  41. {
  42. // 验证 state 防止 CSRF
  43. if ($request->state !== csrf_token()) {
  44. return ['status' => false, 'data' => 'Invalid state parameter'];
  45. }
  46. try {
  47. // 获取短期访问令牌
  48. $response = Http::asForm()->post("https://graph.facebook.com/{$this->graphVersion}/oauth/access_token", [
  49. 'client_id' => env('SSM_FACEBOOK_APP_ID'),
  50. 'client_secret' => env('SSM_FACEBOOK_APP_SECRET'),
  51. 'redirect_uri' => env('DIST_SITE_URL').'/dist/callback/facebook',
  52. 'code' => $request->code,
  53. ]);
  54. if ($response->failed()) {
  55. throw new \Exception($response->json('error.message'));
  56. }
  57. $shortToken = $response->json('access_token');
  58. // 转换为长期令牌
  59. $longTokenResponse = Http::get("https://graph.facebook.com/{$this->graphVersion}/oauth/access_token", [
  60. 'grant_type' => 'fb_exchange_token',
  61. 'client_id' => env('SSM_FACEBOOK_APP_ID'),
  62. 'client_secret' => env('SSM_FACEBOOK_APP_SECRET'),
  63. 'fb_exchange_token' => $shortToken,
  64. ]);
  65. if ($longTokenResponse->failed()) {
  66. throw new \Exception($longTokenResponse->json('error.message'));
  67. }
  68. $longToken = $longTokenResponse->json('access_token');
  69. //当前时间加60天
  70. $expiresAt = Carbon::now()->addDays(60)->format('Y-m-d H:i:s');
  71. // 获取用户信息
  72. $userInfo = $this->getFacebookUser($longToken);
  73. if (!$userInfo['status']) {
  74. return $userInfo;
  75. }
  76. return [
  77. 'status' => true,
  78. 'data' => [
  79. 'accessToken' => $longToken,
  80. 'accessToken_expiresAt' => $expiresAt,
  81. 'userName' => $userInfo['data']['name'],
  82. 'userId' => $userInfo['data']['id']
  83. ]
  84. ];
  85. } catch (\Exception $e) {
  86. return ['status' => false, 'data' => $e->getMessage()];
  87. }
  88. }
  89. public function postImage($message, $imagePaths, $accessToken)
  90. {
  91. try {
  92. $pagesInfo = $this->getAccountsInfo($accessToken);
  93. if (!$pagesInfo['status']) {
  94. return $pagesInfo;
  95. }
  96. $results = [];
  97. $requestContent = [];
  98. $responseContent = [];
  99. foreach ($pagesInfo['data'] as $page) {
  100. $mediaIds = [];
  101. foreach ($imagePaths as $imagePath) {
  102. // 上传图片
  103. $uploadResponse = Http::withToken($page['pageAccessToken'])
  104. ->attach('source', file_get_contents($imagePath), basename($imagePath))
  105. ->post("https://graph.facebook.com/{$this->graphVersion}/{$page['id']}/photos", [
  106. 'published' => false,
  107. ]);
  108. if ($uploadResponse->failed()) {
  109. return ['status' => false, 'data' => $uploadResponse->json('error.message')];
  110. }
  111. $mediaIds[] = ['media_fbid' => $uploadResponse->json('id')];
  112. $requestContent[] = [
  113. 'url'=> "https://graph.facebook.com/{$this->graphVersion}/{$page['id']}/photos",
  114. 'source' => $imagePath,
  115. 'published' => false,
  116. ];
  117. $responseContent[] = $uploadResponse->json();
  118. }
  119. // 发布帖子
  120. $postResponse = Http::withToken($page['pageAccessToken'])
  121. ->post("https://graph.facebook.com/{$this->graphVersion}/{$page['id']}/feed", [
  122. 'message' => $message,
  123. 'attached_media' => json_encode($mediaIds),
  124. ]);
  125. if ($postResponse->failed()) {
  126. return ['status' => false, 'data' => $postResponse->json('error.message')];
  127. }
  128. $results[] = $postResponse->json('id');
  129. $requestContent[] = [
  130. 'url' => "https://graph.facebook.com/{$this->graphVersion}/{$page['id']}/feed",
  131. 'message' => $message,
  132. 'attached_media' => json_encode($mediaIds),
  133. ];
  134. $responseContent[] = $postResponse->json();
  135. }
  136. return ['status' => true,
  137. 'data' => [
  138. 'responseIds' => $results,
  139. 'requestContent' => $requestContent,
  140. 'responseContent' => $responseContent,
  141. ]];
  142. } catch (\Exception $e) {
  143. return ['status' => false, 'data' => $e->getMessage()];
  144. }
  145. }
  146. public function postVideo($message, $videoPath, $accessToken)
  147. {
  148. try {
  149. $pagesInfo = $this->getAccountsInfo($accessToken);
  150. if (!$pagesInfo['status']) {
  151. return $pagesInfo;
  152. }
  153. $results = [];
  154. $requestContent = [];
  155. $responseContent = [];
  156. foreach ($pagesInfo['data'] as $page) {
  157. $response = Http::withToken($page['pageAccessToken'])
  158. ->attach('source', file_get_contents($videoPath), basename($videoPath))
  159. ->post("https://graph.facebook.com/{$this->graphVersion}/{$page['id']}/videos", [
  160. 'description' => $message,
  161. ]);
  162. if ($response->failed()) {
  163. throw new \Exception($response->json('error.message'));
  164. }
  165. $results[] = $response->json('id');
  166. $requestContent[] = [
  167. 'url' => "https://graph.facebook.com/{$this->graphVersion}/{$page['id']}/videos",
  168. 'source' => $videoPath,
  169. 'description' => $message,
  170. ];
  171. $responseContent[] = $response->json();
  172. }
  173. return ['status' => true,
  174. 'data' => [
  175. 'responseIds' => $results,
  176. 'requestContent' => $requestContent,
  177. 'responseContent' => $responseContent,
  178. ]];
  179. } catch (\Exception $e) {
  180. return ['status' => false, 'data' => $e->getMessage()];
  181. }
  182. }
  183. private function getFacebookUser($accessToken)
  184. {
  185. try {
  186. $response = Http::withToken($accessToken)
  187. ->get("https://graph.facebook.com/{$this->graphVersion}/me", [
  188. 'fields' => 'name,id'
  189. ]);
  190. if ($response->failed()) {
  191. throw new \Exception($response->json('error.message'));
  192. }
  193. return [
  194. 'status' => true,
  195. 'data' => $response->json()
  196. ];
  197. } catch (\Exception $e) {
  198. return ['status' => false, 'data' => $e->getMessage()];
  199. }
  200. }
  201. public function getAccountsInfo($accessToken)
  202. {
  203. try {
  204. $response = Http::withToken($accessToken)
  205. ->get("https://graph.facebook.com/{$this->graphVersion}/me/accounts");
  206. if ($response->failed()) {
  207. throw new \Exception($response->json('error.message'));
  208. }
  209. $pages = collect($response->json('data'))->map(function ($page) {
  210. return [
  211. 'id' => $page['id'],
  212. 'name' => $page['name'],
  213. 'pageAccessToken' => $page['access_token'],
  214. ];
  215. })->toArray();
  216. if (empty($pages)) {
  217. throw new \Exception('No pages found');
  218. }
  219. return ['status' => true, 'data' => $pages];
  220. } catch (\Exception $e) {
  221. return ['status' => false, 'data' => $e->getMessage()];
  222. }
  223. }
  224. // 其他接口方法保持空实现
  225. public function getComments($postId) {}
  226. public function replyToComment($commentId) {}
  227. public function deleteComment($commentId) {}
  228. }