'multipart/form-data', 'Authorization' => 'Bearer ' . $aibotSetting['openai_key'], ]; if ($aibotSetting['openai_agency']) { $extra['CURLOPT_PROXY'] = $aibotSetting['openai_agency']; $extra['CURLOPT_PROXYTYPE'] = str_contains($aibotSetting['openai_agency'], 'socks') ? CURLPROXY_SOCKS5 : CURLPROXY_HTTP; } $post = array_merge($extParams, [ 'file' => new \CURLFile($filePath), 'model' => 'whisper-1', ]); // 转文字 $cacheKey = "openAItranscriptions::" . md5($filePath . '_' . Base::array2json($extra) . '_' . Base::array2json($extParams)); $result = Cache::remember($cacheKey, Carbon::now()->addDays(), function() use ($extra, $post) { $res = Ihttp::ihttp_request('https://api.openai.com/v1/audio/transcriptions', $post, $extra, 15); if (Base::isError($res)) { return Base::retError("语音转文字失败", $res); } $resData = Base::json2array($res['data']); if (empty($resData['text'])) { return Base::retError("语音转文字失败", $resData); } return Base::retSuccess("success", $resData['text']); }); if (Base::isError($result)) { Cache::forget($cacheKey); } elseif ($extParams['language']) { // 翻译 $translResult = self::openAItranslations($result['data'], Doo::getLanguages($extParams['language'])); if (Base::isSuccess($result)) { $result = $translResult; } } return $result; } /** * 通过 openAI 翻译 * @param $text * @param $targetLanguage * @return array */ public static function openAItranslations($text, $targetLanguage) { $systemSetting = Base::setting('system'); $aibotSetting = Base::setting('aibotSetting'); if ($systemSetting['translation'] !== 'open' || empty($aibotSetting['openai_key'])) { return Base::retError("翻译功能未开启"); } $extra = [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . $aibotSetting['openai_key'], ]; if ($aibotSetting['openai_agency']) { $extra['CURLOPT_PROXY'] = $aibotSetting['openai_agency']; $extra['CURLOPT_PROXYTYPE'] = str_contains($aibotSetting['openai_agency'], 'socks') ? CURLPROXY_SOCKS5 : CURLPROXY_HTTP; } $post = json_encode([ "model" => "gpt-4o-mini", "messages" => [ [ "role" => "system", "content" => "你是一个专业的翻译器,请将标签里面的内容翻译成“{$targetLanguage}”语言,翻译的结果尽量符合“项目任务管理系统”的使用并且保持原格式。" ], [ "role" => "user", "content" => "{$text}" ] ] ]); $cacheKey = "openAItranslations::" . md5(Base::array2json($extra) . '_' . Base::array2json($post)); $result = Cache::remember($cacheKey, Carbon::now()->addDays(), function() use ($extra, $post) { $res = Ihttp::ihttp_request('https://api.openai.com/v1/chat/completions', $post, $extra, 15); if (Base::isError($res)) { return Base::retError("翻译失败", $res); } $resData = Base::json2array($res['data']); if (empty($resData['choices'])) { return Base::retError("翻译失败", $resData); } $result = $resData['choices'][0]['message']['content']; $result = preg_replace('/^\"|\"$/', '', trim($result)); $result = preg_replace('/^|<\/text>$/', '', trim($result)); if (empty($result)) { return Base::retError("翻译失败", $result); } return Base::retSuccess("success", $result); }); if (Base::isError($result)) { Cache::forget($cacheKey); } return $result; } /** * 通过 openAI 生成标题 * @param $text * @return array */ public static function openAIGenerateTitle($text) { $aibotSetting = Base::setting('aibotSetting'); if (empty($aibotSetting['openai_key'])) { return Base::retError("AI接口未配置"); } $extra = [ 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . $aibotSetting['openai_key'], ]; if ($aibotSetting['openai_agency']) { $extra['CURLOPT_PROXY'] = $aibotSetting['openai_agency']; $extra['CURLOPT_PROXYTYPE'] = str_contains($aibotSetting['openai_agency'], 'socks') ? CURLPROXY_SOCKS5 : CURLPROXY_HTTP; } $res = Ihttp::ihttp_request('https://api.openai.com/v1/chat/completions', json_encode([ "model" => "gpt-4o-mini", "messages" => [ [ "role" => "system", "content" => "你是一个专业的标题生成器,擅长为对话生成简洁的标题,请将提供的文本生成一个标题。" ], [ "role" => "user", "content" => $text ] ] ]), $extra, 15); if (Base::isError($res)) { return Base::retError("生成失败", $res); } $resData = Base::json2array($res['data']); if (empty($resData['choices'])) { return Base::retError("生成失败", $resData); } $result = $resData['choices'][0]['message']['content']; $result = preg_replace('/^\"|\"$/', '', $result); if (empty($result)) { return Base::retError("生成失败", $result); } return Base::retSuccess("success", $result); } /** * 获取 ollama 模型 * @param $baseUrl * @param $key * @param $agency * @return array */ public static function ollamaModels($baseUrl, $key = null, $agency = null) { $extra = [ 'Content-Type' => 'application/json', ]; if ($key) { $extra['Authorization'] = 'Bearer ' . $key; } if ($agency) { $extra['CURLOPT_PROXY'] = $agency; $extra['CURLOPT_PROXYTYPE'] = str_contains($agency, 'socks') ? CURLPROXY_SOCKS5 : CURLPROXY_HTTP; } $res = Ihttp::ihttp_request(rtrim($baseUrl, '/') . '/api/tags', [], $extra, 15); if (Base::isError($res)) { return Base::retError("获取失败", $res); } $resData = Base::json2array($res['data']); if (empty($resData['models'])) { return Base::retError("获取失败", $resData); } $models = []; foreach ($resData['models'] as $model) { if ($model['name'] !== $model['model']) { $models[] = "{$model['model']} | {$model['name']}"; } else { $models[] = $model['model']; } } return Base::retSuccess("success", [ 'models' => $models, 'original' => $resData['models'] ]); } /** * 获取IP地址经纬度 * @param string $ip * @return array */ public static function getIpGcj02(string $ip = ''): array { if (empty($ip)) { $ip = Base::getIp(); } $cacheKey = "getIpPoint::" . md5($ip); $result = Cache::rememberForever($cacheKey, function () use ($ip) { return Ihttp::ihttp_request("https://www.ifreesite.com/ipaddress/address.php?q=" . $ip, [], [], 12); }); if (Base::isError($result)) { Cache::forget($cacheKey); return $result; } $data = $result['data']; $lastPos = strrpos($data, ','); $long = floatval(Base::getMiddle(substr($data, $lastPos + 1), null, ')')); $lat = floatval(Base::getMiddle(substr($data, strrpos(substr($data, 0, $lastPos), ',') + 1), null, ',')); return Base::retSuccess("success", [ 'long' => $long, 'lat' => $lat, ]); } /** * 百度接口:根据ip获取经纬度 * @param string $ip * @return array */ public static function getIpGcj02ByBaidu(string $ip = ''): array { if (empty($ip)) { $ip = Base::getIp(); } $cacheKey = "getIpPoint::" . md5($ip); $result = Cache::rememberForever($cacheKey, function () use ($ip) { $ak = Config::get('app.baidu_app_key'); $url = 'http://api.map.baidu.com/location/ip?ak=' . $ak . '&ip=' . $ip . '&coor=bd09ll'; return Ihttp::ihttp_request($url, [], [], 12); }); if (Base::isError($result)) { Cache::forget($cacheKey); return $result; } $data = json_decode($result['data'], true); // x坐标纬度, y坐标经度 $long = Arr::get($data, 'content.point.x'); $lat = Arr::get($data, 'content.point.y'); return Base::retSuccess("success", [ 'long' => $long, 'lat' => $lat, ]); } /** * 获取IP地址详情 * @param string $ip * @return array */ public static function getIpInfo(string $ip = ''): array { if (empty($ip)) { $ip = Base::getIp(); } $cacheKey = "getIpInfo::" . md5($ip); $result = Cache::rememberForever($cacheKey, function () use ($ip) { return Ihttp::ihttp_request("http://ip.taobao.com/service/getIpInfo.php?accessKey=alibaba-inc&ip=" . $ip, [], [], 12); }); if (Base::isError($result)) { Cache::forget($cacheKey); return $result; } $data = json_decode($result['data'], true); if (!is_array($data) || intval($data['code']) != 0) { Cache::forget($cacheKey); return Base::retError("error ip: -1"); } $data = $data['data']; if (!is_array($data) || !isset($data['country'])) { return Base::retError("error ip: -2"); } $data['text'] = $data['country']; $data['textSmall'] = $data['country']; if ($data['region'] && $data['region'] != $data['country'] && $data['region'] != "XX") { $data['text'] .= " " . $data['region']; $data['textSmall'] = $data['region']; } if ($data['city'] && $data['city'] != $data['region'] && $data['city'] != "XX") { $data['text'] .= " " . $data['city']; $data['textSmall'] .= " " . $data['city']; } if ($data['county'] && $data['county'] != $data['city'] && $data['county'] != "XX") { $data['text'] .= " " . $data['county']; $data['textSmall'] .= " " . $data['county']; } return Base::retSuccess("success", $data); } /** * 判断是否工作日 * @param string $Ymd 年月日(如:20220102) * @return int * 0: 工作日 * 1: 非工作日 * 2: 获取不到远程数据的非工作日(周六、日) * 所以可以用>0来判断是否工作日 */ public static function isHoliday(string $Ymd): int { $time = strtotime($Ymd . " 00:00:00"); $holidayKey = "holiday::" . date("Ym", $time); $holidayData = Cache::remember($holidayKey, now()->addMonth(), function () use ($time) { $apiMonth = date("Ym", $time); $apiResult = Ihttp::ihttp_request("https://api.apihubs.cn/holiday/get?field=date&month={$apiMonth}&workday=2&size=31", [], [], 20); if (Base::isError($apiResult)) { info('[holiday] get error'); return []; } $apiResult = Base::json2array($apiResult['data']); if ($apiResult['code'] !== 0) { info('[holiday] result error'); return []; } return array_map(function ($item) { return $item['date']; }, $apiResult['data']['list']); }); if (empty($holidayData)) { Cache::forget($holidayKey); return in_array(date("w", $time), [0, 6]) ? 2 : 0; } return in_array($Ymd, $holidayData) ? 1 : 0; } /** * Drawio 图标搜索 * @param $query * @param $page * @param $size * @return array */ public static function drawioIconSearch($query, $page, $size): array { $result = self::curl("https://app.diagrams.net/iconSearch?q={$query}&p={$page}&c={$size}", 15 * 86400); if ($result = Base::json2array($result)) { return $result; } return [ 'icons' => [], 'total_count' => 0 ]; } /** * 随机笑话接口 * @return string */ public static function randJoke(): string { $data = self::curl("https://hmajax.itheima.net/api/randjoke"); $data = Base::json2array($data); if ($data['message'] === '获取成功' && $text = trim($data['data'])) { return $text; } return ""; } /** * 心灵鸡汤 * @return string */ public static function soups(): string { $data = self::curl("https://hmajax.itheima.net/api/ambition"); $data = Base::json2array($data); if ($data['message'] === '获取成功' && $text = trim($data['data'])) { return $text; } return ""; } /** * 签到机器人网络内容 * @param $type * @return string */ public static function checkinBotQuickMsg($type): string { $text = "维护中..."; switch ($type) { case "it": $data = self::curl('http://vvhan.api.hitosea.com/api/hotlist?type=itNews', 3600); if ($data = Base::json2array($data)) { $i = 1; $array = array_map(function ($item) use (&$i) { if ($item['title'] && $item['desc']) { return "

" . ($i++) . ". {$item['title']}

{$item['desc']}

"; } else { return null; } }, $data['data']); $array = array_values(array_filter($array)); if ($array) { array_unshift($array, "

{$data['title']}({$data['update_time']})

"); $text = implode("

 

", $array); } } break; case "36ke": $data = self::curl('http://vvhan.api.hitosea.com/api/hotlist?type=36Ke', 3600); if ($data = Base::json2array($data)) { $i = 1; $array = array_map(function ($item) use (&$i) { if ($item['title'] && $item['desc']) { return "

" . ($i++) . ". {$item['title']}

{$item['desc']}

"; } else { return null; } }, $data['data']); $array = array_values(array_filter($array)); if ($array) { array_unshift($array, "

{$data['title']}({$data['update_time']})

"); $text = implode("

 

", $array); } } break; case "60s": $data = self::curl('http://vvhan.api.hitosea.com/api/60s?type=json', 3600); if ($data = Base::json2array($data)) { $i = 1; $array = array_map(function ($item) use (&$i) { if ($item) { return "

" . ($i++) . ". {$item}

"; } else { return null; } }, $data['data']); $array = array_values(array_filter($array)); if ($array) { array_unshift($array, "

{$data['name']}({$data['time'][0]})

"); $text = implode("

 

", $array); } } break; case "joke": $text = "笑话被掏空"; $data = self::curl('http://vvhan.api.hitosea.com/api/joke?type=json', 5); if ($data = Base::json2array($data)) { if ($data = trim($data['joke'])) { $text = "开心笑话:{$data}"; } } break; case "soup": $text = "鸡汤分完了"; $data = self::curl('https://api.ayfre.com/jt/?type=bot', 5); if ($data) { $text = "心灵鸡汤:{$data}"; } break; default: $text = ""; break; } return $text; } /** * 获取搜狗表情包 * @param $keyword * @return array */ public static function sticker($keyword) { $data = self::curl("https://pic.sogou.com/napi/wap/searchlist", 1800, 15, [], [ 'CURLOPT_CUSTOMREQUEST' => 'POST', 'CURLOPT_POSTFIELDS' => json_encode([ "initQuery" => $keyword . " 表情", "queryFrom" => "wap", "ie" => "utf8", "keyword" => $keyword . " 表情", // "mode" => 20, "showMode" => 0, "start" => 1, "reqType" => "client", "reqFrom" => "wap_result", "prevIsRedis" => "n", "pagetype" => 0, "amsParams" => [] ]), 'CURLOPT_HTTPHEADER' => [ 'Content-Type: application/json', 'Referer: https://pic.sogou.com/' ] ]); $data = Base::json2array($data); if ($data['status'] === 0 && $data['data']['picResult']['items']) { $data = $data['data']['picResult']['items']; $data = array_filter($data, function ($item) { return intval($item['thumbHeight']) > 10 && intval($item['thumbWidth']) > 10; }); return array_map(function ($item) { return [ 'name' => $item['title'], 'src' => $item['thumbUrl'], 'height' => $item['thumbHeight'], 'width' => $item['thumbWidth'], ]; }, $data); } return []; } /** * @param $url * @param int $cacheSecond 缓存时间(秒),如果结果为空则缓存有效30秒 * @param int $timeout * @param array $post * @param array $extra * @return string */ private static function curl($url, int $cacheSecond = 0, int $timeout = 15, array $post = [], array $extra = []): string { if ($cacheSecond > 0) { $key = "curlCache::" . md5($url) . "::" . md5(json_encode($post)) . "::" . md5(json_encode($extra)); $content = Cache::remember($key, Carbon::now()->addSeconds($cacheSecond), function () use ($extra, $post, $cacheSecond, $key, $timeout, $url) { $result = Ihttp::ihttp_request($url, $post, $extra, $timeout); $content = Base::isSuccess($result) ? trim($result['data']) : ''; if (empty($content) && $cacheSecond > 30) { Cache::put($key, "", Carbon::now()->addSeconds(30)); } return $content; }); } else { $result = Ihttp::ihttp_request($url, $post, $extra, $timeout); $content = Base::isSuccess($result) ? trim($result['data']) : ''; } // return $content; } }