技术

891 次浏览

通过微信公众号客服 API 集成 OpenAI 的 AI 聊天机器人

有关 ChatGPT 的话题,最近十分火热,通过 Python 脚本编写了一个基于 和 OpenAI 的微信公众号服务器,从而支撑微信公众号集成 OpenAI 的智能聊天机器人的需求。代码如下,

  • 这里通过公众号方式,解决了 OpenAI 被封禁的问题;
  • 通过客服 API 解决了消息超过 5 秒,未回复会被报错的问题。
import threading
# https://github.com/openai/openai-python
# https://platform.openai.com

import openai
# https://github.com/offu/WeRoBot
# https://werobot.readthedocs.io/zh_CN/latest/
# 实例参考 https://blog.csdn.net/IkerIkerIker/article/details/110914641

import werobot
from werobot.replies import TransferCustomerServiceReply

WEB_HOST_URL = "0.0.0.0"
WEB_HOST_PORT = 80
WECHAT_APP_ID = "{{微信公众号的 APP ID}}"
WECHAT_APP_SECRET = "{{微信公众号的 APP SECRET}}"
WECHAT_ENCODING_AES_KEY = "{{微信公众号的 AES 加密密钥}}"
WECHAT_API_TOKEN = "{{微信公众号的 API 设置中配置的 Token,这个可以随意编写}}"
OPEN_AI_API_KEY = "{{ChatGPT 的 API KEY}}"

openai.api_key = OPEN_AI_API_KEY
robot = werobot.WeRoBot(token=WECHAT_API_TOKEN)

def get_chatgpt_response(prompt):
    response = openai.Completion.create(
        model="text-davinci-003",
        prompt=prompt,
        temperature=0,
        max_tokens=1024,
        top_p=1,
        frequency_penalty=0.0,
        presence_penalty=0.0,
    )
    message = response.choices[0].text
    return message.strip()

def post_wechat_customer_service_reply(messages):
    chatGptResponse = get_chatgpt_response(messages.content)
    # https://werobot.readthedocs.io/zh_CN/latest/client.html#
    # https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Service_Center_messages.html
    # 获取 Access Token,发送消息的客服账户 等 API
    return robot.client.send_text_message(user_id=messages.source, content=chatGptResponse, kf_account=None)

@robot.handler
def hello(messages):
    thread = threading.Thread(
        target=post_wechat_customer_service_reply, args=(messages,))
    thread.start()
    # https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Passive_user_reply_message.html
    # 假如服务器无法保证在五秒内处理并回复,必须做出下述回复,这样微信服务器才不会对此作任何处理,
    # 并且不会发起重试(这种情况下,可以使用客服消息接口进行异步回复),否则,将出现严重的错误提示: “该公众号暂时无法提供服务,请稍后再试”
    # 将消息转发至客服:
    # https://developers.weixin.qq.com/doc/offiaccount/Customer_Service/Forwarding_of_messages_to_service_center.html
    return TransferCustomerServiceReply(messages)

robot.config["HOST"] = WEB_HOST_URL
robot.config["PORT"] = WEB_HOST_PORT
robot.config["APP_ID"] = WECHAT_APP_ID
robot.config["APP_SECRET"] = WECHAT_APP_SECRET
robot.config["ENCODING_AES_KEY"] = WECHAT_ENCODING_AES_KEY
robot.run()
1,254 次浏览

中华人民共和国行政区划代码 Excel 整理版

根据中华人民共和国民政部 2020 年 12 月发布的“中华人民共和国县以上行政区划代码”(https://www.mca.gov.cn/article/sj/xzqh/2020/20201201.html),精心核对制作!

形式举例:

行政区划代码 行政区划代码(TEXT) 地区 省/直辖市/自治区 市 区县
210200 210200 辽宁省大连市 辽宁省 大连市
210202 210202 辽宁省大连市中山区 辽宁省 大连市 中山区
210203 210203 辽宁省大连市西岗区 辽宁省 大连市 西岗区
210204 210204 辽宁省大连市沙河口区 辽宁省 大连市 沙河口区
210211 210211 辽宁省大连市甘井子区 辽宁省 大连市 甘井子区
210212 210212 辽宁省大连市旅顺口区 辽宁省 大连市 旅顺口区

Excel Code for Administrative Divisions of PRC

下载地址: https://download.csdn.net/download/nista/85707643?spm=1001.2014.3001.5503

如何通过身份证号链接上述的 Excel 表格获取人员的籍贯?

  • A2 单元格为身份证所在单元格,注意,需要将其设置为 Text 文本 类型,例如,210102199001010011
  • B2 为身份证的前 6 位的行政区划代码,值为 =TEXT(LEFT(A2,6),0)
  • C2 为连接了上述 Excel Sheet 页 “China” 进行籍贯识别后的结果,将它的值设置为以下代码:
=IFERROR(
     VLOOKUP(
        B2,
        China!B:C,
        2,
        0
    ),
     CONCAT( "*(",
         IFERROR(
             VLOOKUP(
                CONCAT(
                    LEFT(
                        A2,
                        4
                    ),
                    "00"
                ),
                China!B:C,
                2,
                0
            ),
             IFERROR(
                 VLOOKUP(
                    CONCAT(
                        LEFT(
                            A2,
                            2
                        ),
                        "0000"
                    ),
                    China!B:C,
                    2,
                    0
                ), "<未找到>" 
            ) 
        ), ")" 
    ) 
)

这段代码会去查找行政区划代码对应的籍贯文字,如果无法找到,则回退到查找市县、如果依然无法查找到,则查询省。

343 次浏览

通过 Git 提交代码时,如何为某一个文件增加可执行权限?

有时候我们可能会提交一些需要在远端(例如 Jenkins CI 服务器等)Shell 环境下执行的脚本文件,我们可以在提交代码时,通过 Git 命令进行权限的修改(即使是在 Windows 环境下)

git update-index –chmod=+x

如果你习惯使用 TortoiseGit 管理你的代码,你也可以通过以下方式添加文件的可执行权限

TortoiseGit Add As Executable

110 次浏览

Azure VM 虚拟机的系统盘占用空间过大,如何裁剪节省成本?

我们在创建 Azure 云的 VM 虚拟机时,其自带的系统盘(C:)往往占用了过大的空间,产生很多额外的开销,我们可以通过如下的 PowerShell 脚本进行空间的裁剪,以优化节约花费!

具体操作步骤,请参考: https://jrudlin.github.io/2019-08-27-shrink-azure-vm-osdisk/

Shrink Azure VM System Disk PowerShell Script:

# Variables
$DiskID = "TODO: 形式如同: /subscriptions/203bdbf0-69bd-1a12-a894-a826cf0a34c8/resourcegroups/rg-server1-prod-1/providers/Microsoft.Compute/disks/Server1-Server1"
$VMName = "TODO: Azure VM NAME VVVVVV"
$DiskSizeGB = 32
$AzSubscription = "TODO: Azure Subscription NAME SSSSSS"

# Script
# Provide your Azure admin credentials
Connect-AzAccount

#Provide the subscription Id of the subscription where snapshot is created
Select-AzSubscription -Subscription $AzSubscription

# VM to resize disk of
$VM = Get-AzVm | ? Name -eq $VMName

#Provide the name of your resource group where snapshot is created
$resourceGroupName = $VM.ResourceGroupName

# Get Disk from ID
$Disk = Get-AzDisk | ? Id -eq $DiskID

# Get VM/Disk generation from Disk
$HyperVGen = $Disk.HyperVGeneration

# Get Disk Name from Disk
$DiskName = $Disk.Name

# Get SAS URI for the Managed disk
$SAS = Grant-AzDiskAccess -ResourceGroupName $resourceGroupName -DiskName $DiskName -Access 'Read' -DurationInSecond 600000;

#Provide the managed disk name
#$managedDiskName = "yourManagedDiskName" 

#Provide Shared Access Signature (SAS) expiry duration in seconds e.g. 3600.
#$sasExpiryDuration = "3600"

#Provide storage account name where you want to copy the snapshot - the script will create a new one temporarily
$storageAccountName = "shrink" + [system.guid]::NewGuid().tostring().replace('-','').substring(1,18)

#Name of the storage container where the downloaded snapshot will be stored
$storageContainerName = $storageAccountName

#Provide the key of the storage account where you want to copy snapshot. 
#$storageAccountKey = "yourStorageAccountKey"

#Provide the name of the VHD file to which snapshot will be copied.
$destinationVHDFileName = "$($VM.StorageProfile.OsDisk.Name).vhd"

#Generate the SAS for the managed disk
#$sas = Grant-AzureRmDiskAccess -ResourceGroupName $resourceGroupName -DiskName $managedDiskName -Access Read -DurationInSecond $sasExpiryDuration

#Create the context for the storage account which will be used to copy snapshot to the storage account 
$StorageAccount = New-AzStorageAccount -ResourceGroupName $resourceGroupName -Name $storageAccountName -SkuName Standard_LRS -Location $VM.Location
$destinationContext = $StorageAccount.Context
$container = New-AzStorageContainer -Name $storageContainerName -Permission Off -Context $destinationContext

#Copy the snapshot to the storage account and wait for it to complete
Start-AzStorageBlobCopy -AbsoluteUri $SAS.AccessSAS -DestContainer $storageContainerName -DestBlob $destinationVHDFileName -DestContext $destinationContext
while(($state = Get-AzStorageBlobCopyState -Context $destinationContext -Blob $destinationVHDFileName -Container $storageContainerName).Status -ne "Success") { $state; Start-Sleep -Seconds 20 }
$state

# Revoke SAS token
Revoke-AzDiskAccess -ResourceGroupName $resourceGroupName -DiskName $DiskName

# Emtpy disk to get footer from
$emptydiskforfootername = "$($VM.StorageProfile.OsDisk.Name)-empty.vhd"

# Empty disk URI
#$EmptyDiskURI = $container.CloudBlobContainer.Uri.AbsoluteUri + "/" + $emptydiskforfooter

$diskConfig = New-AzDiskConfig `
    -Location $VM.Location `
    -CreateOption Empty `
    -DiskSizeGB $DiskSizeGB `
    -HyperVGeneration $HyperVGen

$dataDisk = New-AzDisk `
    -ResourceGroupName $resourceGroupName `
    -DiskName $emptydiskforfootername `
    -Disk $diskConfig

$VM = Add-AzVMDataDisk `
    -VM $VM `
    -Name $emptydiskforfootername `
    -CreateOption Attach `
    -ManagedDiskId $dataDisk.Id `
    -Lun 63

Update-AzVM -ResourceGroupName $resourceGroupName -VM $VM

$VM | Stop-AzVM -Force


# Get SAS token for the empty disk
$SAS = Grant-AzDiskAccess -ResourceGroupName $resourceGroupName -DiskName $emptydiskforfootername -Access 'Read' -DurationInSecond 600000;

# Copy the empty disk to blob storage
Start-AzStorageBlobCopy -AbsoluteUri $SAS.AccessSAS -DestContainer $storageContainerName -DestBlob $emptydiskforfootername -DestContext $destinationContext
while(($state = Get-AzStorageBlobCopyState -Context $destinationContext -Blob $emptydiskforfootername -Container $storageContainerName).Status -ne "Success") { $state; Start-Sleep -Seconds 20 }
$state

# Revoke SAS token
Revoke-AzDiskAccess -ResourceGroupName $resourceGroupName -DiskName $emptydiskforfootername

# Remove temp empty disk
Remove-AzVMDataDisk -VM $VM -DataDiskNames $emptydiskforfootername
Update-AzVM -ResourceGroupName $resourceGroupName -VM $VM

# Delete temp disk
Remove-AzDisk -ResourceGroupName $resourceGroupName -DiskName $emptydiskforfootername -Force;

# Get the blobs
$emptyDiskblob = Get-AzStorageBlob -Context $destinationContext -Container $storageContainerName -Blob $emptydiskforfootername
$osdisk = Get-AzStorageBlob -Context $destinationContext -Container $storageContainerName -Blob $destinationVHDFileName

$footer = New-Object -TypeName byte[] -ArgumentList 512
write-output "Get footer of empty disk"

$downloaded = $emptyDiskblob.ICloudBlob.DownloadRangeToByteArray($footer, 0, $emptyDiskblob.Length - 512, 512)

$osDisk.ICloudBlob.Resize($emptyDiskblob.Length)
$footerStream = New-Object -TypeName System.IO.MemoryStream -ArgumentList (,$footer)
write-output "Write footer of empty disk to OSDisk"
$osDisk.ICloudBlob.WritePages($footerStream, $emptyDiskblob.Length - 512)

Write-Output -InputObject "Removing empty disk blobs"
$emptyDiskblob | Remove-AzStorageBlob -Force


#Provide the name of the Managed Disk
$NewDiskName = "$DiskName" + "-new"

#Create the new disk with the same SKU as the current one
$accountType = $Disk.Sku.Name

# Get the new disk URI
$vhdUri = $osdisk.ICloudBlob.Uri.AbsoluteUri

# Specify the disk options
$diskConfig = New-AzDiskConfig -AccountType $accountType -Location $VM.location -DiskSizeGB $DiskSizeGB -SourceUri $vhdUri -CreateOption Import -StorageAccountId $StorageAccount.Id -HyperVGeneration $HyperVGen

#Create Managed disk
$NewManagedDisk = New-AzDisk -DiskName $NewDiskName -Disk $diskConfig -ResourceGroupName $resourceGroupName

$VM | Stop-AzVM -Force

# Set the VM configuration to point to the new disk  
Set-AzVMOSDisk -VM $VM -ManagedDiskId $NewManagedDisk.Id -Name $NewManagedDisk.Name

# Update the VM with the new OS disk
Update-AzVM -ResourceGroupName $resourceGroupName -VM $VM

$VM | Start-AzVM

start-sleep 180
# Please check the VM is running before proceeding with the below tidy-up steps

# Delete old Managed Disk
Remove-AzDisk -ResourceGroupName $resourceGroupName -DiskName $DiskName -Force;

# Delete old blob storage
$osdisk | Remove-AzStorageBlob -Force

# Delete temp storage account
$StorageAccount | Remove-AzStorageAccount -Force
59 次浏览

低碳理念与软件研发的关系?

对于可持续性(Sustainability)、环保、低碳的概念,我们总会局限于包装、环境污染、材料等等主题。然而,其实作为软件研发或产品设计者,我们的细心思考或优化同样能够带来对于低碳理念的实际践行。

您可以参考近期由 Wunderman Thompson 发布的《2022全球百大趋势》(Topic 13,page 32)有关“碳中和网页”(Carbon-Neutral Net)的概念。根据有关电子邮件污染的报告指出,如果互联网是一个国家,那么电子邮件就是世界第六大污染源。无用邮件每年仅仅在英国就相当于产生 200 万吨的二氧化碳排放。

通过对网页/软件/逻辑设计的优化,可以显著减少站点平均每次浏览产生的二氧化碳量。许多企业正在尝试对其网站、电子邮件进行持续地改进,以减少其在线活动的碳足迹。

你可以通过这个网站,估算您当前所关心的 Website 的碳足迹。

167 次浏览

OAuth 2.0 Implicit Grant Flow “隐式授权流” 不再被推荐!

Apple Safari 已经提供默认启用的隐私保护功能,称为智能跟踪保护 (ITP)。 ITP 会阻止“第三方”的 Cookie – 包含在跨域请求中的 Cookie。
常见的用户跟踪形式是:在后台将一个 iframe 加载到第三方站点,并使用 Cookie 在整个 Internet 中关联用户。【遗憾的是,此模式也是单页应用 (SPA) 中实现Oauth 2.0 的隐式流(Implicit Grant Flow)的标准方式】

当浏览器阻止第三方 Cookie 以阻止用户跟踪时,SPA 也会中断。【并非只有 Safari】通过阻止第三方 Cookie 来增强用户隐私保护。Brave 默认已阻止第三方 Cookie,而 Chromium(基于 Google Chrome 和 Microsoft Edge 的平台)也已宣布,在 2023 年年底停止支持第三方 Cookie。

因为上述的从浏览器中删除第三方 Cookie 的行为,隐式授权流不再是合适的身份验证方法。 如果没有第三方 Cookie,隐式流的无提示 SSO 功能将不起作用,导致应用程序在尝试获取新令牌时中断。 强烈建议你让所有新的应用程序都使用 【授权代码流(Authorization Code Flow)】,并让现有的单页应用也开始迁移到授权代码流。

这对于所有基于 SPA 开发并包含联合认证登录方式的网站,【都会产生重大影响】,需要你对项目进行重大重构和修改。

相关参考

Apple Safari 已经启用此限制,Chromium 将在 2023 年底前启用。这是一个类似于“微软将会从 Windows 中移除 IE” 一个级别的信息,请大家务必注意。

345 次浏览

获取项目中全部 NuGet 包以及对应的 License

我们在日常 .NET Core 项目开发的过程中,经常需要获得整个解决方案中所有项目所引用的 NuGet 包的列表、对应的 License,甚至有时候出于法律的考量,我们需要检查 NuGet 包中所有递归引用的内部包的 License。

如果我们手动进行检查,工作量是巨大的,因此我编写了一个 PowerShell 脚本,来自动化完成这个工作。

基本原理

首先,基于 dotnet 命令自带的 list package 方法,及 –include-transitive 参数,来获取全部包引用的列表。
随后,通过 WebClient 下载对应的 License 文件。

使用方法

将 .ps1 文件放置到项目解决方案文件 (.sln) 所在位置并运行即可。

*

In the process of daily .NET Core project development, we often need to obtain a list of NuGet packages referenced by all projects in the solution, corresponding licenses, and sometimes for legal considerations, we need to check all licenses of recursion internal packages in each directly referenced NuGet packages.

If we check it manually, the workload is huge, so I wrote a PowerShell script to automate this task.

Fundamental

It’s based on the list package method that comes with the dotnet command, and the –include-transitive parameter, to obtain a list of all package references. And then, download the corresponding license file through WebClient.

Instructions

Place the .ps1 file in the location of the project solution file (.sln) and run it.

*

# Place the file to .sln folder, and run

dotnet list package --include-transitive > NuGetPackageList.txt

mkdir ".\licenses" > nul 2> nul
echo "Package ID,Package Version,Package Spec URL,License URL" > LicenseList.csv

Select-String "(>)\s+(\S+)\s+(\S+)\s+(\S+)*" NuGetPackageList.txt  |  Select @{Name="Id"; Expression = {$_.Matches.Groups[2]}}, @{Name="Version"; Expression = {$_.Matches.Groups[3]}}  |  Sort-Object -Property @{Expression="Id"; Descending=$false}, @{Expression="Version"; Descending=$false} -unique | % {

    $packageId = $_.Id;
    $packageIdLowerCase = $packageId.ToString().ToLower();
    $packageVersion = $_.Version;
    $packageSpecUrl = "https://api.nuget.org/v3-flatcontainer/$packageIdLowerCase/$packageVersion/$packageIdLowerCase.nuspec"

    Write-Host
    Write-Host("o> $packageId")
    Write-Host("  $packageVersion`n  $packageSpecUrl") -ForegroundColor Green 

    Try {  
        # Refer to https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-manifest-nuspec
        [xml]$packageSpec = (New-Object System.Net.WebClient).DownloadString($packageSpecUrl);
        $packageLicenseUrl = $packageSpec.package.metadata.licenseUrl;

        Write-Host("  $packageLicenseUrl") -ForegroundColor Green 
        echo "$packageId,$packageVersion,$packageSpecUrl,$packageLicenseUrl" >> LicenseList.csv
        
        Try {
            $filePath = (Join-Path (pwd) 'licenses\') + "$packageId.$packageVersion.txt";
            (New-Object System.Net.WebClient).DownloadFile($packageLicenseUrl, $filePath);
        }
        Catch [system.exception] {
            # Write-Host ($error[0].Exception);
            Write-Host ("!!> Could not download license file for $packageId $packageVersion") -ForegroundColor DarkMagenta
        }
    }
    Catch [system.exception] {
        # Write-Host ($error[0].Exception);
        Write-Host ("!!> Could not read license for $packageId $packageVersion") -ForegroundColor Red
        echo "$packageId,$packageVersion,$packageSpecUrl,(none)" >> LicenseList.csv
    }

}
266 次浏览

VMware 提示与 Device/Credential Guard 不兼容

当我们在较新的 Windows 10 版本 (1909 或更新)使用 VMware Workstation(无论是 Pro 还是 Player 版本)时,会出现 WMware 与 Windows 自带的 Hyper-V 组件互不兼容的问题,从而报错:

“VMware Workstation and Device/Credential Guard are not compatible. VMware Workstation can be run after disabling Device/Credential Guard. Please visit http://www.vmware.com/go/turnoff_CG_DG for more details.”

解决方法:

  1. 开始 – 运行 (Win + R) – 键入 cmd as admin
  2. 在命令提示符键入 bcdedit /set hypervisorlaunchtype off
  3. 重启电脑,即可。

存在的问题:

禁用 Hyper-V 将直接导致依赖于 Hyper-V 的 Docker Desktop 无法正常启用,我们可以下载体验(Edge)版的 Docker Desktop 来禁用与 Hyper-V 相关的功能,并改用 Windows Container。

相关资料:

https://stackoverflow.com/questions/50374506/vmware-workstation-cannot-run-on-windows-10-after-recent-update-to-windows-10
https://docs.microsoft.com/en-us/virtualization/windowscontainers/quick-start/set-up-environment?tabs=Windows-10-Client&redirectedfrom=MSDN
https://www.cnblogs.com/ups216/p/6385663.html