国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

數(shù)字人系列四:Motionverse 接入chatgpt、文心一言等國內(nèi)外大語言模型

這篇具有很好參考價值的文章主要介紹了數(shù)字人系列四:Motionverse 接入chatgpt、文心一言等國內(nèi)外大語言模型。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

1. 下載插件:motionverse官網(wǎng)地址:概述?·?Motionverse?接口文檔?(deepscience.cn)

數(shù)字人系列四:Motionverse 接入chatgpt、文心一言等國內(nèi)外大語言模型,chatgpt,語言模型,人工智能


2. 按照官方文檔新建Unity工程:對接說明?·?Motionverse?接口文檔?(deepscience.cn)


3. 通過我們自己的ASR,將語音轉(zhuǎn)換為文本,文本通過大語言模型(chatgpt、文心一言以及其他大語言模型)生成的結(jié)果,直接通過生成式AI技術(shù),把文本轉(zhuǎn)化為AI智能體的聲音、動作和表情,和大語言模型完美連接,只需要在獲取到文本的時候調(diào)用以下代碼即可:
代碼示例:

DriveTask task = new DriveTask();
task.player = player;
task.text = question;
NLPDrive.GetDrive(task);


其中,player即為掛載motionverse插件中Player腳本的對象,question即為大語言模型獲取到的答案。

還可以自己生成語音,通過語音鏈接調(diào)用以下代碼:

DriveTask task = new DriveTask();

task.player = player;

task.text = audioUrl;

AudioUrlDrive.GetDrive(task);

其中,player即為掛載motionverse插件中Player腳本的對象,audioUrl即為語音鏈接。

4. 新建腳本AskManager,并掛載到場景中(可新建空物體),腳本代碼如下:

using LitJson;
using Motionverse;
using MotionverseSDK;
using System;
using System.Collections;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;

public class AskManager : MonoBehaviour
{
    public Player player;
    public Dropdown dropdown;
    public InputField inputField;
    public Button btnSend;

    public string chatGptKey = "";
    private string chatUrl = "https://chatgpt.kazava.io/v1/chat/completions";

    public string wenXinAPI = "";
    public string wenXinSECRET = "";
    private string wenXinToken = "";
    private int curSelectNLP = 0;

    // Start is called before the first frame update
    private void Awake()
    {
        for (int i = 0; i < Display.displays.Length; i++)
        {
            Display.displays[i].Activate();
        }

    }
    void Start()
    {
        dropdown.onValueChanged.AddListener((value) =>
        {
            curSelectNLP = value;
        });
        StartCoroutine(GetWenxinToken());
        btnSend.onClick.AddListener(() =>
        {
            if (string.IsNullOrEmpty(inputField.text))
            {
                Debug.Log("請輸入內(nèi)容!");
            }
            else
            {
                GetAnswer(inputField.text);
            }
        });
    }

    public void GetAnswer(string q)
    {
        StartCoroutine(RealAnswer(q));
    }



    public IEnumerator RealAnswer(string question)
    {
        switch (curSelectNLP)
        {
            case 0:
                DriveTask task = new DriveTask();
                task.player = player;
                task.text = question;
                NLPDrive.GetDrive(task);
                break;
            case 1:
                StartCoroutine(RequestChat(question));
                break;
            case 2:
                StartCoroutine(ChatCompletions(question));
                break;
            default:
                break;
        }

        Invoke("restartRecording", 1);

        yield return null;

    }

    IEnumerator GetWenxinToken()
    {
        string url =$"https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id={wenXinAPI}&client_secret={wenXinSECRET}";
        UnityWebRequest webRequest = UnityWebRequest.Get(url);
        webRequest.timeout = 5000;

        yield return webRequest.SendWebRequest();

        if (webRequest.result == UnityWebRequest.Result.Success)
        {
            string response = webRequest.downloadHandler.text;
            var result = JsonUtility.FromJson<AccessTokenResponse>(response);
            wenXinToken = result.access_token;
        }
        else
        {
            Debug.LogError("Failed to get access token: " + webRequest.error);
        }
    }

    IEnumerator ChatCompletions(string content)
    {
        string url = $"https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions?access_token={wenXinToken}";
        WenXinPostData postData = new WenXinPostData();
        postData.messages = new WenXinMessage[1];
        postData.messages[0] = new WenXinMessage();
        postData.messages[0].content = content + "30字以內(nèi)";
        string data = JsonMapper.ToJson(postData);

        UnityWebRequest webRequest = UnityWebRequest.Post(url, data, "application/json");
        webRequest.timeout = 5000;
        yield return webRequest.SendWebRequest();

        if (webRequest.result == UnityWebRequest.Result.Success)
        {
            string response = webRequest.downloadHandler.text;
            WenXinRequest requestData = JsonMapper.ToObject<WenXinRequest>(response);
            DriveTask task = new DriveTask();
            task.player = player;
            task.text = requestData.result;
            TextDrive.GetDrive(task);
        }
        else
        {
            Debug.LogError("Chat completions request failed: " + webRequest.error);
        }
    }

    private IEnumerator RequestChat(string content)
    {
        using (UnityWebRequest webRequest = new UnityWebRequest(chatUrl, "POST"))
        {
            webRequest.SetRequestHeader("Content-Type", "application/json");
            ChatGPTPostData postData = new ChatGPTPostData();
            postData.key = chatGptKey;
            postData.messages = new PostMessage[1];
            postData.messages[0] = new PostMessage();
            postData.messages[0].content = content + "30字以內(nèi)"; ;
            string data = JsonMapper.ToJson(postData);
            byte[] jsonToSend = new UTF8Encoding().GetBytes(data);
            webRequest.uploadHandler = new UploadHandlerRaw(jsonToSend);
            webRequest.downloadHandler = new DownloadHandlerBuffer();
            yield return webRequest.SendWebRequest();

            if (webRequest.result != UnityWebRequest.Result.Success)
            {
                Debug.LogError("ChatGPT request error: " + content + webRequest.error);
            }
            else
            {
                string response = webRequest.downloadHandler.text;
                ChatGPTRequestData requestData = JsonMapper.ToObject<ChatGPTRequestData>(response);
                DriveTask task = new DriveTask();
                task.player = player;
                task.text = requestData.choices[0].message.content;
                TextDrive.GetDrive(task);
            }
        }
    }
}


5. 新建腳本RealtimeAsrManager,代碼如下:

using System;
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityWebSocket;


namespace Motionverse
{
    public class RealtimeAsrManager : MonoBehaviour
    {
        private IWebSocket webSocket;
        private Status status = Status.FirstFrame;
        private bool lockReconnect = false;
        [HideInInspector]
        public static Action<string> Asr;

        [SerializeField]
        private GameObject TextBG;
        //百度
        public int AsrAppId;
        public string AsrAppkey = "";


        void Start()
        {
            CreateWebSocket();
            RecorderManager.DataAvailable += OnDataAvailable;
        }
        private void OnDisable()
        {
            RecorderManager.DataAvailable -= OnDataAvailable;
        }
        void CreateWebSocket()
        {
            try
            {
                string uril = "wss://vop.baidu.com/realtime_asr?sn=" + Guid.NewGuid().ToString();
                webSocket = new WebSocket(uril);
                InitHandle();
                webSocket.ConnectAsync();
            }
            catch (Exception e)
            {
                Debug.Log("websocket連接異常:" + e.Message);
                ReConnect();
            }

        }


        private void InitHandle()
        {
            RemoveHandle();
            webSocket.OnOpen += OnOpen;
            webSocket.OnMessage += OnMessage;
            webSocket.OnClose += OnClose;
            webSocket.OnError += OnError;


        }
        void RemoveHandle()
        {
            webSocket.OnOpen -= OnOpen;
            webSocket.OnMessage -= OnMessage;
            webSocket.OnClose -= OnClose;
            webSocket.OnError -= OnError;
        }
        //請求開始
        private void OnDataAvailable(byte[] data)
        {
            if (webSocket == null || (webSocket != null && webSocket.ReadyState != WebSocketState.Open))
                return;

            switch (status)
            {
                case Status.FirstFrame://握手
                    {
                        var firstFrame = new FirstFrame();
                        firstFrame.data.appid= AsrAppId;
                        firstFrame.data.appkey = AsrAppkey;

                        webSocket.SendAsync(JsonUtility.ToJson(firstFrame));
                        status = Status.ContinueFrame;
                    }
                    break;
                case Status.ContinueFrame://開始發(fā)送
                    {
                        if (data.Length > 0)
                        {
                            webSocket.SendAsync(data);
                        }
                    }
                    break;
                case Status.LastFrame://關(guān)閉
                    {
                        webSocket.SendAsync(JsonUtility.ToJson(new LastFrame()));
                    }
                    break;
                default:
                    break;
            }
        }

        void ReConnect()
        {
            if (this.lockReconnect)
                return;
            this.lockReconnect = true;
            StartCoroutine(SetReConnect());
        }

        private IEnumerator SetReConnect()
        {
            yield return new WaitForSeconds(1);
            CreateWebSocket();
            lockReconnect = false;
        }

        #region WebSocket Event Handlers

        private void OnOpen(object sender, OpenEventArgs e)
        {
            status = Status.FirstFrame;
        }

        private void OnMessage(object sender, MessageEventArgs e)
        {
            var err_msg = Utils.GetJsonValue(e.Data, "err_msg");
            var type = Utils.GetJsonValue(e.Data, "type");
           
            if (err_msg == "OK" && type == "MID_TEXT")
            {
                var result = Utils.GetJsonValue(e.Data, "result");
                TextBG.GetComponentInChildren<Text>().text = result;
            }

            if (err_msg == "OK" && type == "FIN_TEXT")
            {
                var result = Utils.GetJsonValue(e.Data, "result");
                TextBG.GetComponentInChildren<Text>().text = result;
                if (result.Length > 1)
                {
                    RecorderManager.Instance.EndRecording();
                    Asr?.Invoke(result);
                }
               
            }

        }

        private void OnClose(object sender, CloseEventArgs e)
        {
            Debug.Log("websocket關(guān)閉," + string.Format("Closed: StatusCode: {0}, Reason: {1}", e.StatusCode, e.Reason));
            webSocket = null;
            ReConnect();
        }

        private void OnError(object sender, ErrorEventArgs e)
        {
            if (e != null)
                Debug.Log("websocket連接異常:" + e.Message);
            webSocket = null;
            ReConnect();
        }

        #endregion
    }
}


6. 新建腳本RecorderManager,代碼如下:

using UnityEngine;
using System;
using System.Collections;
using UnityEngine.UI;
using Unity.VisualScripting;
namespace Motionverse
{
    public class RecorderManager : Singleton<RecorderManager>
    {
        //標(biāo)記是否有麥克風(fēng)
        private bool isHaveMic = false;
        //當(dāng)前錄音設(shè)備名稱
        private string currentDeviceName = string.Empty;
        //表示錄音的最大時長
        int recordMaxLength = 600;
        //錄音頻率,控制錄音質(zhì)量(16000)
        int recordFrequency = 16000;
        [HideInInspector]
        public static Action<byte[]> DataAvailable;
        [SerializeField]
        private GameObject micON;
        [SerializeField]
        private GameObject micOFF;
        [SerializeField]
        private Image micAmount;
        [SerializeField]
        private GameObject TextBG;
        private AudioClip saveAudioClip;

        int offsetSamples = 0;
        private void Start()
        {
            Debug.Log(Microphone.devices[0]);
            if (Microphone.devices.Length > 0)
            {
                isHaveMic = true;
                currentDeviceName = Microphone.devices[0];
                StartCoroutine(GetAudioFrames());
                StartRecording();
            }

        }

        /// <summary>
        /// 開始錄音
        /// </summary>
        /// <returns></returns>
        public void StartRecording() //16000
        {
            if (isHaveMic == false || Microphone.IsRecording(currentDeviceName))
            {
                return;
            }
            micOFF.gameObject.SetActive(false);
            micON.gameObject.SetActive(true);
            TextBG.GetComponentInChildren<Text>().text = null;
            offsetSamples = 0;
            saveAudioClip = Microphone.Start(currentDeviceName, true, recordMaxLength, recordFrequency);

        }
        public void OnButtonClick()
        {
            if (Microphone.IsRecording(currentDeviceName))
            {
                EndRecording();
            }
            else
            {
                StartRecording();
            }
        }
        public void EndRecording()
        {

            if (isHaveMic == false || !Microphone.IsRecording(currentDeviceName))
            {
                return;
            }
            micOFF.gameObject.SetActive(true);
            micON.gameObject.SetActive(false);
            //結(jié)束錄音
            Microphone.End(currentDeviceName);
        }
        public bool IsRecording()
        {
            return Microphone.IsRecording(currentDeviceName);
        }
        IEnumerator GetAudioFrames()
        {

            while (true)
            {
                if (Microphone.IsRecording(currentDeviceName))
                {
                    int lenght = Microphone.GetPosition(currentDeviceName) * saveAudioClip.channels - offsetSamples;
                    if (lenght > 0)
                    {
                        float[] samples = new float[lenght];
                        saveAudioClip.GetData(samples, offsetSamples);

                        var samplesShort = new short[samples.Length];
                        for (var index = 0; index < samples.Length; index++)
                        {
                            samplesShort[index] = (short)(samples[index] * short.MaxValue);
                        }
                        byte[] binaryData = new byte[samplesShort.Length * 2];
                        Buffer.BlockCopy(samplesShort, 0, binaryData, 0, binaryData.Length);

                        offsetSamples += lenght;
                        DataAvailable?.Invoke(binaryData);
                    }

                    yield return new WaitForSeconds(0.16f);
                }
                else
                {
                    yield return new WaitForSeconds(0.16f);
                    byte[] binaryData = new byte[2];
                    DataAvailable?.Invoke(binaryData);
                }

            }
        }

        /// <summary>
        /// 獲取毫秒級別的時間戳,用于計算按下錄音時長
        /// </summary>
        /// <returns></returns>
        public double GetTimestampOfNowWithMillisecond()
        {
            return (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000;
        }

        private void LateUpdate()
        {
            if (isHaveMic == true && Microphone.IsRecording(currentDeviceName))
            {
                micAmount.fillAmount = Volume;
            }
        }
        public float Volume
        {
            get
            {
                if (Microphone.IsRecording(currentDeviceName))
                {
                    // 采樣數(shù)
                    int sampleSize = 128;
                    float[] samples = new float[sampleSize];
                    int startPosition = Microphone.GetPosition(currentDeviceName) - (sampleSize + 1);
                    // 得到數(shù)據(jù)
                    if (startPosition < 0)
                        return 0;

                    saveAudioClip.GetData(samples, startPosition);

                    // Getting a peak on the last 128 samples
                    float levelMax = 0;
                    for (int i = 0; i < sampleSize; ++i)
                    {
                        float wavePeak = samples[i];
                        if (levelMax < wavePeak)
                            levelMax = wavePeak;
                    }

                    return levelMax;
                }
                return 0;
            }
        }
        void OnGUI()
        {
            GUIStyle guiStyle = GUIStyle.none;
            guiStyle.fontSize = 10;
            guiStyle.normal.textColor = Color.white;
            guiStyle.alignment = TextAnchor.UpperLeft;
            Rect tr = new Rect(0, 0, 100, 100);
            GUI.Label(tr, currentDeviceName, guiStyle);
        }
    }
}


7. 新建空物體,掛載腳本RecorderManager和RealtimeAsrManager
8. 輸入百度asr的appId和secretKey:

數(shù)字人系列四:Motionverse 接入chatgpt、文心一言等國內(nèi)外大語言模型,chatgpt,語言模型,人工智能


9. 輸入GPTkey、問心一眼appId和secretKey:

數(shù)字人系列四:Motionverse 接入chatgpt、文心一言等國內(nèi)外大語言模型,chatgpt,語言模型,人工智能


10. 根據(jù)需求選擇相應(yīng)的NLP方式:

數(shù)字人系列四:Motionverse 接入chatgpt、文心一言等國內(nèi)外大語言模型,chatgpt,語言模型,人工智能


注意:如果缺少Singleton文件,可使用如下代碼:

using UnityEngine;
namespace Motionverse
{
    public abstract class Singleton<T> : MonoBehaviour where T : Singleton<T>
    {

        private static T sInstance = null;

        public static T Instance
        {
            get
            {
                if (sInstance == null)
                {
                    GameObject gameObject = new(typeof(T).FullName);
                    sInstance = gameObject.AddComponent<T>();
                }

                return sInstance;
            }
        }

        public static void Clear()
        {
            sInstance = null;
        }

        protected virtual void Awake()
        {
            if (sInstance != null) Debug.LogError(name + "error: already initialized", this);

            sInstance = (T)this;
        }
    }
}

若有收獲,就點個贊吧文章來源地址http://www.zghlxwxcb.cn/news/detail-694401.html

到了這里,關(guān)于數(shù)字人系列四:Motionverse 接入chatgpt、文心一言等國內(nèi)外大語言模型的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • AI百度文心一言大語言模型接入使用(中國版ChatGPT)

    AI百度文心一言大語言模型接入使用(中國版ChatGPT)

    基于百度文心一言語言大模型的智能文本對話AI機(jī)器人API,支持聊天對話、行業(yè)咨詢、語言學(xué)習(xí)、代碼編寫等功能. 重要提示:建議使用https協(xié)議,當(dāng)https協(xié)議無法使用時再嘗試使用http協(xié)議 請求方式: POST 序號 參數(shù) 是否必須 說明 1 ques 是 你的問題 2 appKey 是 唯一驗證AppKey, 可前往官

    2024年02月12日
    瀏覽(34)
  • 檢測文本是否由AI生成,GPT、文心一言等均能被檢測

    檢測文本是否由AI生成,GPT、文心一言等均能被檢測

    目前很多機(jī)構(gòu)推出了ChatGPT等AI文本檢測工具,但是準(zhǔn)確率主打一個模棱兩可,基本和拋硬幣沒啥區(qū)別。 先說結(jié)論,我們對比了常見的幾款A(yù)I檢測工具,copyleaks檢測相比較而言最準(zhǔn)確。 來源:GPT3.5 提問詞:Redis有什么作用? Redis是一種開源的內(nèi)存數(shù)據(jù)庫,它具有多種作用和用途

    2024年02月14日
    瀏覽(23)
  • GBASE首批接入中國版ChatGPT“文心一言“ 打造DB+AI全系服務(wù)

    3月 9日,GBASE南大通用宣布成為百度文心一言(英文名:ERNIE Bot)首批生態(tài)合作伙伴。后續(xù), GBASE將通過百度智能云全面體驗并集成“文心一言”的AI能力,聚焦DB+AI全棧技術(shù)及服務(wù)。 文心一言是基于百度智能云技術(shù)打造出來的大模型。百度在人工智能領(lǐng)域深耕十余年,擁有產(chǎn)

    2024年02月11日
    瀏覽(19)
  • 百度“文心一言”首批生態(tài)合作伙伴公布,Moka接入打造人力資源數(shù)字化人AI服務(wù)

    百度“文心一言”首批生態(tài)合作伙伴公布,Moka接入打造人力資源數(shù)字化人AI服務(wù)

    近日,百度“文心一言” ( 英文名:ERNIE Bot ) 公布 首批生態(tài)合作伙伴,企業(yè)級HR SaaS服務(wù)商Moka 位列其中 , 將優(yōu)先體驗并接入“文心一言”,以此打造更創(chuàng)新、更智能的人力資源數(shù)字化服務(wù)。 “文心一言”是百度基于文心大模型技術(shù)推出的生成式對話產(chǎn)品。百度在人工智

    2024年02月15日
    瀏覽(20)
  • Java接入文心一言

    首先需要先申請文心千帆大模型,申請地址:文心一言 (baidu.com),點擊加入體驗,等通過審核之后就可以進(jìn)入文心千帆大模型后臺進(jìn)行應(yīng)用管理了。 在百度智能云首頁即可看到文心千帆大模型平臺 然后進(jìn)入后臺管理之后,點擊應(yīng)用接入,創(chuàng)建應(yīng)用即可 創(chuàng)建完應(yīng)應(yīng)用之后,便

    2024年02月10日
    瀏覽(20)
  • 文心一言api接入如何在你的項目里使用文心一言

    文心一言api接入如何在你的項目里使用文心一言

    基于百度文心一言語言大模型的智能文本對話AI機(jī)器人API,支持聊天對話、行業(yè)咨詢、語言學(xué)習(xí)、代碼編寫等功能. 重要提示:建議使用https協(xié)議,當(dāng)https協(xié)議無法使用時再嘗試使用http協(xié)議 請求方式: POST 序號 參數(shù) 是否必須 說明 1 ques 是 你的問題 2 appKey 是 唯一驗證AppKey, 可前往官

    2024年02月09日
    瀏覽(25)
  • nodejs文心一言API接入

    nodejs文心一言API接入

    需求 在nodejs里面接入文心一言API,官方調(diào)用步驟API介紹 - 千帆大模型平臺 | 百度智能云文檔 大致流程 創(chuàng)建應(yīng)用——API授權(quán)——獲取訪問憑證——調(diào)用接口 創(chuàng)建應(yīng)用 注冊賬號創(chuàng)建應(yīng)用 首先注冊百度云智能賬號,登錄進(jìn)入百度智能云千帆控制臺?,然后進(jìn)入控制臺創(chuàng)建應(yīng)用?。

    2024年02月03日
    瀏覽(20)
  • 文心一言AI大模型,前端接入

    文心一言AI大模型,前端接入 一、參考接口資料 模型廣場:https://console.bce.baidu.com/qianfan/modelcenter/model/buildIn/list 我的應(yīng)用:https://console.bce.baidu.com/qianfan/ais/console/onlineService 千帆大模型調(diào)用API介紹: https://cloud.baidu.com/doc/WENXINWORKSHOP/s/flfmc9do2 在線調(diào)試: https://console.bce.baidu.co

    2024年02月04日
    瀏覽(29)
  • 百度文心一言接入教程-Java版

    百度文心一言接入教程-Java版

    原文鏈接 前段時間由于種種原因我的AI BOT https://chat.jylt.top網(wǎng)站停運了數(shù)天,后來申請了百度的文心一言和阿里的通義千問開放接口,文心一言的接口很快就通過了,但是文心一言至今杳無音訊。文心一言通過審之后,很快將AI BOT的AI能力接入了文心一言,這里記錄一下具體的

    2024年02月15日
    瀏覽(13)
  • 使用文心一言等智能工具指數(shù)級提升嵌入式/物聯(lián)網(wǎng)(M5Atom/ESP32)和機(jī)器人操作系統(tǒng)(ROS1/ROS2)學(xué)習(xí)研究和開發(fā)效率

    使用文心一言等智能工具指數(shù)級提升嵌入式/物聯(lián)網(wǎng)(M5Atom/ESP32)和機(jī)器人操作系統(tǒng)(ROS1/ROS2)學(xué)習(xí)研究和開發(fā)效率

    以M5AtomS3為例,博客撰寫效率提升10倍以上: 0.?Linux環(huán)境Arduino IDE中配置ATOM S3_zhangrelay的博客-CSDN博客 1.?M5ATOMS3基礎(chǔ)01按鍵_zhangrelay的博客-CSDN博客 2.?M5ATOMS3基礎(chǔ)02傳感器MPU6886_zhangrelay的博客-CSDN博客 3.?M5ATOMS3基礎(chǔ)03給ROS1發(fā)一個問候(rosserial)_zhangrelay的博客-CSDN博客 4.?M5ATOMS3基

    2024年02月14日
    瀏覽(35)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包