飞扬 / AIDeepSeek 公开
main
AIDeepSeek/Plugin.php
Plugin.php13.2 KB
<?php
/**
 * Typecho AI写作助手插件(DeepSeek增强版)
 * 
 * @package AIDeepSeek
 * @author 小野博客
 * @version 1.1.1
 * @link https://lb5.net
 */
class AIDeepSeek_Plugin implements Typecho_Plugin_Interface
{
    /**
     * 激活插件方法
     */
    public static function activate()
    {
        // 注册方式
        Typecho_Plugin::factory('admin/write-post.php')->bottom = array(__CLASS__, 'render');
        
        return "插件启用成功,请配置DeepSeek API参数";
    }

    /**
     * 禁用插件方法
     */
    public static function deactivate()
    {
        return "插件已禁用";
    }

    /**
     * 获取插件配置面板
     */
    public static function config(Typecho_Widget_Helper_Form $form)
    {
        $div = new Typecho_Widget_Helper_Layout();
        $div->html('
            <div style="background:#f8f9fa; padding:15px; border-radius:5px; margin-bottom:20px;">
                <h5>DeepSeek API 配置</h5>
                <p>1. 获取API密钥: 访问 <a href="https://platform.deepseek.com/api-keys" target="_blank">DeepSeek API Keys</a></p>
                <p>2. 模型选择: deepseek-chat (通用) 或 deepseek-coder (编程)</p>
                <p>3. 原创性参数: 控制创造性 (0.1-1.0)</p>
            </div>
        ');
        $div->render();
        
        // DeepSeek API Key
        $apiKey = new Typecho_Widget_Helper_Form_Element_Text('apiKey', null, null,
            _t('API密钥'), _t('在此处输入DeepSeek API密钥'));
        $form->addInput($apiKey->addRule('required', _t('API密钥不能为空')));
        
        // 模型选择
        $model = new Typecho_Widget_Helper_Form_Element_Select('model', 
            array(
                'deepseek-chat' => 'deepseek-chat (通用写作)',
                'deepseek-coder' => 'deepseek-coder (技术文章)'
            ), 
            'deepseek-chat',
            _t('模型选择'), _t('根据写作内容选择合适模型'));
        $form->addInput($model);
        
        // 参数 - 修复浮点数验证问题
        $temperature = new Typecho_Widget_Helper_Form_Element_Text('temperature', null, '0.7',
            _t('原创性 (0.1-1.0)'), _t('值越高生成内容越有原创性'));
        $form->addInput($temperature);
    }

    /**
     * 个人用户的配置面板
     */
    public static function personalConfig(Typecho_Widget_Helper_Form $form)
    {
        // 无需个人配置
    }

    /**
     * 插件输出方法
     */
    public static function render()
    {
        // 获取插件配置
        $options = Helper::options()->plugin('AIDeepSeek');
        $apiKey = $options->apiKey;
        $model = $options->model;
        $temperature = $options->temperature;
        
        // 参数格式
        $tempValue = is_numeric($temperature) ? floatval($temperature) : 0.7;
        if ($tempValue < 0.1 || $tempValue > 1.0) {
            $tempValue = 0.7;
        }
        
        // 安全输出JavaScript
        $apiKeyJS = htmlspecialchars($apiKey ?? '', ENT_QUOTES);
        $modelJS = htmlspecialchars($model ?? '', ENT_QUOTES);
        $temperatureJS = number_format($tempValue, 1); // 确保格式化为浮点数
        
        // 输出HTML和JavaScript
        echo <<<HTML
            <style>
                .deepseek-panel {
                    background: #f8f9fa;
                    padding: 15px;
                    border-radius: 5px;
                    margin: 20px 0;
                    border: 1px solid #e1e4e8;
                }
                .deepseek-input {
                    width: 100%;
                    padding: 8px 12px;
                    border: 1px solid #ced4da;
                    border-radius: 4px;
                    margin-bottom: 10px;
                }
                .deepseek-btn {
                    padding: 8px 15px;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                    font-weight: 500;
                    transition: all 0.2s;
                    margin-right: 10px;
                }
                .deepseek-primary {
                    background-color: #2563eb;
                    color: white;
                }
                .deepseek-primary:hover {
                    background-color: #1d4ed8;
                }
                .deepseek-secondary {
                    background-color: #6c757d;
                    color: white;
                }
                .deepseek-secondary:hover {
                    background-color: #5a6268;
                }
                .deepseek-spinner {
                    display: inline-block;
                    width: 12px;
                    height: 12px;
                    border: 2px solid rgba(255,255,255,0.3);
                    border-radius: 50%;
                    border-top-color: #fff;
                    animation: spin 1s linear infinite;
                    margin-right: 5px;
                    vertical-align: middle;
                }
                @keyframes spin {
                    to { transform: rotate(360deg); }
                }
            </style>
            
            <script>
                // 显示/隐藏AI面板
                function toggleAIPanel() {
                    const panel = document.getElementById("deepseek-controls");
                    const button = document.getElementById("deepseek-toggle-btn");
                    
                    if(panel.style.display === "none" || !panel.style.display){
                        panel.style.display = "block";
                        button.textContent = "收起AI写作";
                    } else {
                        panel.style.display = "none";
                        button.textContent = "使用AI写作";
                    }
                }
               
                // 显示遮罩层
                function showOverlay() {
                    const overlay = document.getElementById("deepseek-overlay");
                    overlay.style.display = "block";
                }
                
                // 隐藏遮罩层
                function hideOverlay() {
                    const overlay = document.getElementById("deepseek-overlay");
                    overlay.style.display = "none";
                }
                
                // 生成内容
                function generateContent() {
                    const keywords = document.getElementById('deepseek-keywords').value;
                    const prompt = document.getElementById('deepseek-prompt').value;
                    const btn = document.getElementById('deepseek-generate-btn');
                    const originalText = btn.innerHTML;
                    
                    if(!keywords.trim()) {
                        alert('请至少输入一个关键词');
                        return;
                    }
                    
                    // 显示加载状态
                    btn.innerHTML = '<span class="deepseek-spinner"></span> 生成中...';
                    btn.disabled = true;
                    showOverlay();
                    
                    // 组装提示词
                    const fullPrompt = "你是一位专业作家,请根据以下关键词和要求创作一篇文章:\\n\\n" +
                                      "关键词:" + keywords + "\\n" +
                                      "具体要求:" + (prompt || "生成一篇结构完整、语言流畅的文章");
                    
                    // 创建请求数据
                    const requestData = {
                        model: "{$modelJS}",
                        messages: [
                            {role: "system", content: "你是专业的写作助手"},
                            {role: "user", content: fullPrompt}
                        ],
                        temperature: {$temperatureJS}, 
                        max_tokens: 2000,
                        stream: false
                    };
                    
                    // 发送请求到DeepSeek API
                    fetch('https://api.deepseek.com/chat/completions', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                            'Authorization': 'Bearer {$apiKeyJS}'
                        },
                        body: JSON.stringify(requestData)
                    })
                    .then(response => {
                        if(!response.ok) {
                            throw new Error('API响应错误: ' + response.status);
                        }
                        return response.json();
                    })
                    .then(data => {
                        if(data.choices && data.choices.length > 0) {
                            let content = data.choices[0].message.content;
                            // 清理Markdown格式
                            content = content.replace(/【.*?】/g, '');
                            document.getElementById("deepseek-output").value = content;
                        } else {
                            throw new Error('未获取到有效内容: ' + JSON.stringify(data));
                        }
                    })
                    .catch(error => {
                        alert('请求失败: ' + error.message);
                        document.getElementById("deepseek-output").value = '生成失败: ' + error.message;
                    })
                    .finally(() => {
                        btn.innerHTML = originalText;
                        btn.disabled = false;
                        hideOverlay();
                    });
                }
                
                // 复制内容到剪贴板
                function copyContent() {
                    const textarea = document.getElementById('deepseek-output');
                    textarea.select();
                    document.execCommand('copy');
                    alert('内容已复制到剪贴板');
                }
            </script>
            
            <!-- 遮罩层 -->
            <div id="deepseek-overlay" style="
                display: none;
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background: rgba(0,0,0,0.5);
                z-index: 9999;
                justify-content: center;
                align-items: center;
            ">
                <div style="
                    background: white;
                    padding: 30px;
                    border-radius: 8px;
                    text-align: center;
                    box-shadow: 0 4px 20px rgba(0,0,0,0.15);
                ">
                    <div class="deepseek-spinner" style="
                        width: 30px;
                        height: 30px;
                        border-width: 3px;
                        display: block;
                        margin: 0 auto 15px;
                    "></div>
                    <p style="font-size: 16px; margin: 0;">DeepSeek AI正在写作...</p>
                </div>
            </div>
            
            <div style="margin:20px 0;">
                <button id="deepseek-toggle-btn" class="deepseek-btn deepseek-primary" 
                        onclick="toggleAIPanel()" style="font-size:14px;">
                    使用AI写作
                </button>
            </div>
            
            <div id="deepseek-controls" class="deepseek-panel" style="display:none;">
                <div>
                    <input type="text" placeholder="文章主题/关键词(必填)" 
                           class="deepseek-input" id="deepseek-keywords">
                    
                    <textarea placeholder="具体要求(可选,如:文章风格、结构、字数等)" 
                              class="deepseek-input" id="deepseek-prompt"
                              rows="3"></textarea>
                    
                    <div>
                        <button id="deepseek-generate-btn" class="deepseek-btn deepseek-primary" 
                                onclick="generateContent()">
                            生成文章内容
                        </button>
                        <button class="deepseek-btn deepseek-secondary" 
                                onclick="copyContent()">
                            复制到剪贴板
                        </button>
                    </div>
                </div>
            </div>
            
            <div class="deepseek-panel">
                <textarea autocomplete="off" id="deepseek-output" class="deepseek-input" 
                          rows="8" placeholder="AI生成的内容将显示在这里"></textarea>
            </div>
HTML;
    }
    
    /**
     * 插件头部输出方法(空实现)
     */
    public static function header() {}
    
    /**
     * 插件底部输出方法(空实现)
     */
    public static function footer() {}
}