Category: Office work

  • SSPAI Morning Brief: Samsung Unveils 2026 Odyssey Series Monitors, and More

    SSPAI Morning Brief: Samsung Unveils 2026 Odyssey Series Monitors, and More

    Morning Brief

    1. Samsung unveils 2026 Odyssey series monitors
    2. Spotify bans accounts linked to Anna’s Archive
    3. FCC bans all foreign drones from entering the U.S. market
    4. Paramount steps up its bid for Warner Bros.
    5. DingTalk releases AI DingTalk 1.1 “Mulan”
    6. 007: First Light release delayed
    7. Rumors You Can Just Glance At

    Samsung Unveils 2026 Odyssey Series Monitors

    On December 24, Samsung announced the launch of five 2026 Odyssey series gaming monitors, including the Odyssey G6, Odyssey 3D, and Odyssey G8.

    Among them, the 32-inch, 6K-resolution Odyssey 3D (model G90XH) supports glasses-free 3D and features real-time eye tracking, adjusting visual depth and angles based on the viewer’s eye position. It offers dual refresh-rate modes of 165Hz and 330Hz, with a 1ms GtG response time. The first batch of games optimized for its glasses-free 3D experience includes The First Berserker: Khazan, Stellar Blade, and Lies of P: Overture.

    Odyssey 3D

    The 27-inch Odyssey G6 (model G60H) supports dual refresh-rate modes, offering QHD resolution at 600Hz and HD resolution at 1040Hz. The 32-inch Odyssey G8 (model G80HS) operates in dual modes of 6K resolution at 165Hz and 3K resolution at 330Hz. The 27-inch Odyssey G8 (model G80H) supports dual modes of 5K resolution at 180Hz and QHD resolution at 360Hz.

    The 32-inch Odyssey OLED G8 (model G80SH) features a 4K-resolution QD-OLED panel with a 240Hz refresh rate and 300 nits of brightness. It carries VESA DisplayHDR™ True Black 500 certification and, via the DP 2.1 (UHBR20) standard, supports video transmission bandwidths of up to 80Gbps. All models are compatible with both AMD FreeSync Premium and NVIDIA G-Sync, and will be showcased at CES 2026 in Las Vegas in January 2026. Source


    Spotify bans accounts linked to Anna’s Archive

    On December 22, Spotify confirmed that it has disabled user accounts associated with the piracy organization Anna’s Archive. The group claimed over the weekend that it had scraped approximately 86 million audio files and metadata for 256 million tracks from Spotify, stating that the data represents more than 99.6% of Spotify’s listening activity and describing the action as the creation of an “open music archive.” Source


    FCC bans all foreign drones from entering the U.S. market

    On Monday, the U.S. Federal Communications Commission (FCC) added all foreign-manufactured drones and key components to its “Covered List” of untrusted suppliers, deeming these unmanned aircraft systems to pose an “unacceptable risk to U.S. national security.” The move effectively blocks drone products from non-U.S. manufacturers—including DJI—from entering the U.S. market.

    The measure took effect immediately on December 22, 2025, one day ahead of the deadline previously set by Congress for a national security determination on Chinese-made drones. After the ban came into force, new drone models and related components on the list can no longer obtain the FCC equipment authorization required for import and sale. Drone models that were already imported, authorized, and sold are not affected and may continue to be used. In a statement, DJI said that “the administration has not disclosed the information on which it based its decision.” Source

    On December 23, a spokesperson for China’s Ministry of Commerce said the country firmly opposes the move, arguing that the U.S. is overstretching the concept of national security and using state power to suppress foreign companies, including Chinese enterprises—calling it a典型 example of market distortion and unilateral bullying. The spokesperson urged the U.S. to stop its erroneous practices and immediately withdraw the measures. Source


    Paramount steps up its bid for Warner Bros.

    After Warner Bros. Discovery announced a deal with Netflix and rejected a hostile takeover bid from Paramount, Paramount has shifted to making a direct offer to shareholders in an effort to further optimize its proposal and push the transaction forward.

    Paramount, led by David Ellison, said that his father—Oracle founder and billionaire Larry Ellison—will provide an irrevocable personal guarantee valued at $40.4 billion. Paramount stated that its revised proposal offers Warner Bros. Discovery greater flexibility in debt refinancing arrangements, representations, and interim operating covenants. To demonstrate the Ellison family’s ability to fund the $40.4 billion portion of the all-cash $108 billion acquisition offer, the new proposal also disclosed records showing that the Ellison family trust holds 1.16 billion shares of Oracle stock.

    Paramount also announced that if the deal is blocked during regulatory review, the breakup fee it would pay to Warner Bros. Discovery would be increased from the previous $5 billion to $5.8 billion, matching the level offered by Netflix. Source


    DingTalk releases AI DingTalk 1.1 “Mulan”

    On December 23, DingTalk held the AI DingTalk 1.1 product launch and ecosystem conference in Hangzhou, announcing its upgrade from a traditional mobile internet application to an AI-native operating system (Agent OS). At the event, DingTalk unveiled multiple new products, including DingTalk Real, a new terminal designed for enterprise agents that offers real-time data access, permission-controlled execution, and process monitoring—providing a trusted runtime environment for enterprise-grade agents. It also introduced Enterprise Agent OS and the agent orchestration platform “Wukong,” which can coordinate multimodal models, enterprise systems, and various agents to enable cross-business automated collaboration.

    In addition, DingTalk rolled out a range of industry-specific intelligent agents such as AI Printing, AI Recruitment, and AI Travel, and showcased real-world applications including order recognition and quality inspection in manufacturing. AI Sheets, AI Meeting Notes, and the collaborative translation tool DingTalk A1 also received multimodal upgrades. DingTalk further announced plans to launch a dual-version strategy—separate China and overseas editions of DingTalk—to meet global business needs and strengthen compliance capabilities. Source


    007: First Light release delayed

    IO Interactive has announced that its upcoming title 007: First Light has been delayed to May 27, 2026. The game was originally scheduled for release in March 2026. IO Interactive said that while the game is already highly playable, the team wants to use the additional two months for final polishing and optimization, and promised to share more information in early 2026.

    007: First Light is the first game based on the James Bond IP in nearly a decade. Developed by the studio behind the Hitman series, it focuses on the origin story of James Bond before he earns the 007 designation and becomes a legendary agent. Source


    Rumors You Can Just Glance At

    On Tuesday, the U.S. Department of Justice released nearly 30,000 pages of documents related to Jeffrey Epstein, including an email from a prosecutor that reveals former U.S. President Donald Trump flew on Epstein’s private jet eight times in the 1990s—contradicting Trump’s claim on social media in 2024 that he had “never flown on Epstein’s plane.”

    According to three sources familiar with the matter, NVIDIA has informed Chinese customers that it plans to begin shipping its H200 AI chips in mid-February 2026, ahead of the Lunar New Year holiday. Separately, MLex reports that Alibaba is also considering purchasing between 40,000 and 50,000 MI308 AI chips from AMD. The MI308 is designed specifically for the Chinese market, aiming to comply with U.S. export regulations while still supporting large-scale AI training and inference workloads.

    Marvel Studios officially released the first trailer for Avengers: Doomsday on December 23, confirming that Chris Evans will return as Steve Rogers. Back in January, Evans had said in an interview that he had happily bid farewell to Marvel Studios and dismissed rumors of a return.

  • Give Your Mac Mail a “Brain”: Automatic Fetching, Summarizing, and Reporting

    Give Your Mac Mail a “Brain”: Automatic Fetching, Summarizing, and Reporting

    Although many email clients or plugins already offer per-email summarization and translation, they often lack the ability to generate a global summary of all emails received that day. So, by combining the built-in Mail app on macOS with the Kouzi agent, I created the following workflow:

    “Use AppleScript to fetch email content from the Mail app, call the Kouzi agent to summarize it, and send the summarized results back to my own inbox—executed automatically every day.”

    The implementation can be broken down into three steps:

    1. Fetching email content
    2. Calling the Kouzi agent to summarize
    3. Setting up automation for scheduled execution

    The final effect: every day, you receive a neatly timed email containing the summarized content of all your unread messages, as shown below.

    Temporary older emails were used for testing, so the content appears dated.

    Fetching email content

    To avoid disrupting the reading flow, I’ve placed the full AppleScript used to fetch email content at the end of the article. Here, I’ll highlight the key ideas:

    1. I specified the iCloud account and the newsletter folder in the script, because that’s where all my subscription emails are collected.
    2. The script retrieves only unread emails and marks them as read after successfully extracting their content.
    3. The extracted content is saved as .txt files in a designated folder, using the email subject as the filename. This keeps data storage simple and decouples it from the later AI-processing steps, making debugging easier.
    4. For each email, the script obtains its message-ID and converts it into a URI of the format message://%3c'messageID'%3e. Clicking this URI launches Mail and opens the corresponding email in a pop-up window—very handy for jumping to the original message after reviewing the summary.

    At the end of this step, we have successfully fetched the content of all unread emails in the designated folder and saved them as text files.

    Using the Kouzi Agent to Summarize Email Content

    I use a JavaScript script to read the files saved in the previous step and call the Kouzi API to generate summaries. Of course, you can use DeepSeek or any other provider’s API. I chose Kouzi mainly for its asynchronous query feature. The full script is also included at the end of the article.

    Script Logic

    1. Read all files in the target directory: the script scans through every file in the specified folder.
    2. Filter for .txt files created today: only files created on the current day with a .txt extension are processed.
    3. Read file content and call the conversation API: for each valid file, the script reads the content and submits it to the Kouzi chat API.
    4. Poll until AI processing completes: using the chat/retrieve endpoint, the script continuously checks the processing status until it becomes completed.
    5. Fetch and organize AI results: through the chat/message/list endpoint, the script retrieves the AI’s response and formats it as HTML, accumulating everything into an allContents variable.
    6. Send an email once all files are processed: after processing all valid files, the script uses nodemailer to send an email containing all summaries generated that day.
    7. Email includes unread count and AI summaries: the subject is the current date, and the body includes the number of unread emails plus all AI-generated summaries.

    Kouzi Agent Setup and Invocation
    Create an agent inside the Kouzi workspace and configure the agent’s prompt. In the address bar of that setup page, you’ll find the Bot_ID, which is used as a parameter when calling the API.

    Kouzi API calls consist of three steps: initiating a conversation, checking the status, and retrieving the result.

    The first step, initiating a conversation, is essentially submitting a processing request to Kouzi.

    Here you need to provide your API Key, Bot_ID, and a custom User_ID. Kouzi will return a Chat_ID and a Conversation_ID as unique identifiers.

    The second step, checking the status, is like asking Kouzi whether the request has finished processing. Only after receiving a completed status should you fetch the result; otherwise, you may end up retrieving incomplete output.

    At this stage, you must provide the Chat_ID and Conversation_ID returned in the previous step.

    The third step, retrieving the result, is where you obtain the final output from the agent. You again supply the Chat_ID and Conversation_ID from step one to fetch the completed response.

    Configure Automation for Scheduled Execution

    With the AppleScript for fetching email content and the JavaScript script for calling the Kouzi agent ready, the next step is to create an automated workflow that runs them regularly.

    Open the Automator app and create a new Calendar Alarm workflow:

    Add a Run AppleScript action, paste in your AppleScript code, and remember to wrap it with the execution block, like this:

    on run {input, parameters}
        -- AppleScript code
    end run

    Add a Run Shell Script action, navigate to the folder containing your script, and execute the JavaScript file:

    cd /path/to/script
    /opt/homebrew/bin/node scriptName.js

    Add a Show Notification action to confirm the workflow has finished running.

    The Automator configuration looks like this:

    After creating it, open the Calendar app and make a new event. Set it to repeat daily, and under the Alert section choose:
    Custom → Open File → Other… → select your Automator workflow.
    This way, it will run automatically each day.

    With this, the entire setup is complete. However, be mindful of privacy and security when using it—it’s best to apply this workflow only to newsletter or public information emails, and avoid uploading private messages containing sensitive personal data.

    AppleScript for Fetching Email Content

    tell application "Mail"
    	-- Get the "newsletter" mailbox under the iCloud account
    	set theAccount to account "iCloud"
    	set theMailbox to mailbox "newsletter" of theAccount
    
    	-- Get all unread emails in the "newsletter" mailbox
    	set unreadMessages to (every message of theMailbox whose read status is false)
    
    	-- Iterate through all unread emails
    	repeat with eachMessage in unreadMessages
    		-- Use the email subject as part of the filename
    		set theSubject to subject of eachMessage
    		-- Get the email content and convert it to plain text
    		set theContent to content of eachMessage
    		set plainTextContent to do shell script "echo " & quoted form of theContent & " | textutil -convert txt -stdin -stdout"
    
    		-- Get the message-ID and generate a URI
    		set messageID to message id of eachMessage
    		set messageURL to "message://" & "%3c" & messageID & "%3e"
    	
    		-- Create a filename by removing illegal characters
    		set cleanSubject to do shell script "echo " & quoted form of theSubject & " | tr -d '\"/:<>?\\|+[]{};=,'"
    		set fileName to (cleanSubject & ".txt")
    	
    		-- Use the specified POSIX path and convert it to AppleScript path format
    		set savePath to POSIX file "path to save txt files"
    	
    		-- Full file path
    		set filePath to (savePath as string) & fileName
    	
    		-- Write the email content to the txt file at the specified path
    		try
    			set fileReference to open for access file filePath with write permission
    			write plainTextContent to fileReference starting at eof
    	
    			-- Append the email URI at the end of the file
    			write return & "emailURL=" & messageURL to fileReference starting at eof
    			close access fileReference
    		on error errMsg
    			close access file filePath
    			display dialog "Error: " & errMsg
    		end try
    	
    		-- Mark the email as read
    		set read status of eachMessage to true
    	end repeat
    end tell
    
    return input

    Script for Calling the Kouzi Agent

    const fs = require('fs');
    const path = require('path');
    const axios = require('axios');
    const nodemailer = require('nodemailer');
    
    const directoryPath = 'Directory path to store text files'; // The directory path to store text files
    const today = new Date().toLocaleDateString('zh-CN'); // Get system local date
    
    let allContents = ''; // Used to store all returned contents from the query API
    let fileCounter = 0; // Used for file counting
    let validFileCount = 0; // Valid file count
    let emailSent = false; // A flag to track whether the email has been sent
    
    // Read files under the specified directory
    fs.readdir(directoryPath, async (err, files) => {
      if (err) {
        console.error('Failed to read directory:', err);
        return;
      }
    
      const filePromises = files.map(file => processFile(file));
      await Promise.all(filePromises);
      checkAllFilesProcessed();
    });
    
    async function processFile(file) {
      const filePath = path.join(directoryPath, file);
    
      try {
        const stats = await fs.promises.stat(filePath);
        const fileCreationDate = stats.birthtime.toLocaleDateString('zh-CN'); // Get file creation date
        if (fileCreationDate === today && path.extname(file) === '.txt') {
          validFileCount++; // Increase valid file count
          const content = await fs.promises.readFile(filePath, 'utf8');
          await callAIAPI(content, file);
        }
      } catch (err) {
        console.error('Error while processing file:', err);
      }
    }
    
    // Use async/await to optimize async handling
    async function callAIAPI(content, fileName) {
      const apiUrl = 'https://api.coze.cn/v3/chat';
      const headers = getHeaders();
      
      // Choose bot_id depending on content length
      const botId = content.length > 32000 ? 'bot_id' : 'bot_id';
      
      const data = {
        bot_id: botId, // Dynamic bot_id
        user_id: '**',
        stream: false,
        auto_save_history: true,
        additional_messages: [
          {
            role: 'user',
            content: content,
            content_type: 'text'
          }
        ]
      };
    
      let attempts = 0;
      const maxAttempts = 5;
    
      while (attempts < maxAttempts) {
        try {
          const response = await axios.post(apiUrl, data, { headers, timeout: 5000 });
          console.log('API response:', response.data);
          const { id, conversation_id } = response.data.data;
          await new Promise(resolve => setTimeout(resolve, 1000)); // Add wait time: 1 second
          await pollConversationStatus(id, conversation_id, fileName);
          break;
        } catch (error) {
          attempts++;
          console.error(`API call error (attempt ${attempts}/${maxAttempts}):`, error);
          if (attempts >= maxAttempts) {
            console.error('Still failed after multiple attempts');
          }
        }
      }
    }
    
    // Extract headers function
    function getHeaders() {
      return {
        'Authorization': 'Bearer api_key',
        'Content-Type': 'application/json'
      };
    }
    
    // Poll conversation detail interface
    async function pollConversationStatus(chat_id, conversation_id, fileName) {
      const retrieveUrl = `https://api.coze.cn/v3/chat/retrieve?chat_id=${chat_id}&conversation_id=${conversation_id}`;
      const headers = getHeaders();
    
      let pollCount = 0;
      const maxPollCount = 120;
    
      return new Promise((resolve, reject) => {
        const intervalId = setInterval(async () => {
          pollCount++;
          if (pollCount > maxPollCount) {
            clearInterval(intervalId);
            reject(new Error(`Polling timeout, chat_id: ${chat_id}, conversation_id: ${conversation_id}`));
            return;
          }
    
          try {
            const response = await axios.get(retrieveUrl, { headers });
            // Cancel printing poll logs
            // console.log('Dialog detail API response:', response.data);
            if (response.data.data.status === 'completed') {
              clearInterval(intervalId);
              await queryAIAPI(chat_id, conversation_id, fileName);
              resolve();
            }
          } catch (error) {
            console.error('Dialog detail API error:', error);
            clearInterval(intervalId);
            reject(error);
          }
        }, 1000); // Poll every second
      });
    }
    
    // Call query interface
    async function queryAIAPI(chat_id, conversation_id, fileName) {
      const queryUrl = `https://api.coze.cn/v3/chat/message/list?chat_id=${chat_id}&conversation_id=${conversation_id}`;
      const headers = getHeaders();
    
      try {
        const response = await axios.get(queryUrl, { headers });
        console.log('Query API response:', response.data);
        const contents = response.data.data
          .filter(item => item.type === 'answer') // Filter items of type 'answer'
          .map(item => item.content)
          .join('\n');
    
        // Read last line of file to get MessageID
        const filePath = path.join(directoryPath, fileName);
        const fileContent = await fs.promises.readFile(filePath, 'utf8');
        const lastLine = fileContent.trim().split('\n').pop();
        const messageId = lastLine.split('=')[1]; // Extract MessageID
    
        fileCounter++; // Increase file count
        console.log(`File count: ${fileCounter}`);
        const fileNameWithoutExt = fileName.replace(/\.[^/.]+$/, ""); // Remove extension
        allContents += `<h3><a href="${messageId}">${fileCounter}. ${fileNameWithoutExt}</a></h3><br>${contents.replace(/\n/g, '<br>')}<br><br>`;
      } catch (error) {
        console.error('Query API error:', error);
      }
    }
    
    // Check if all files are processed
    function checkAllFilesProcessed() {
      if (fileCounter === validFileCount && !emailSent) {
        emailSent = true; // Mark email as sent
        sendEmail(allContents); // Call send email function
      }
    }
    
    // Send email
    function sendEmail(contents) {
      const transporter = nodemailer.createTransport({
        host: 'smtp.qq.com',
        port: 465,
        secure: true, // Use SSL
        auth: {
          user: 'Email address', // Your email address
          pass: 'Email authorization code' // Your email authorization code
        }
      });
    
      const mailOptions = {
        from: 'Email address', // Sender email address
        to: 'Email address',    // Recipient email address
        subject: `${today} Daily Email Summary`, // Email subject
        html: `Unread email count: ${validFileCount}<br><br>${contents}` // HTML content
      };
    
      transporter.sendMail(mailOptions, (error, info) => {
        if (error) {
          return console.error('Failed to send email:', error);
        }
        console.log('Email sent:', info.response);
      });
    }