SecureCRT自动化脚本

本文详细介绍了Crt编程中Dialog对象的使用,包括FileOpenDialog、MessageBox、Prompt以及Screen对象的属性和方法,如CurrentColumn、CurrentRow等,以及如何进行屏幕数据操作和控制。
摘要由CSDN通过智能技术生成

脚本文件头

脚本中头两行(必须写在头两行,且最好不要有空格回车之类的字符)所必须添加的标识,这两行标识告诉Crt当前执行的脚本需要使用什么解释器进行解释。其中language为语言,现支持Jscript、VBScript、Python、Perl,interface。

JS

# $language = "JScript"
# $interface = "1.0"

python

# $language = "python"
# $interface = "1.0"

crt的属性

Dialog

介绍

定义了一个Dialog(对话框),通过这个对象,可以通过调用该对象的子属性和方法实现对话框的相关功能。

JS

var diaObj = crt.Dialog;

Dialog的方法

FileOpenDialog

解释
定义了一个Dialog(对话框),通过这个对象,可以通过调用该对象的子属性和方法实现对话框的相关功能。

语法

crt.Dialog.Method([arglist])

参数
(1)title:弹窗最上面的标题文字,见运行结果中窗口最上面的"请选择一个文件"。
(2)buttonLabel:见下图中的"Open"按钮,即打开文件用的,至于名称可以使用"Open"或者"打开"均可。
(3)defaultFilename:默认文件名,如下,在弹窗的"文件名"中默认有"a.log"。注意:当有默认文件名时即使当前文件夹中没有该文件,在点击"打开"按钮后,结果依然会返回该文件的当前目录的绝对路径。
(4)filter:用于过滤文件类型,见脚本举例中的格式,"Log Files (.log)|.log,| 的前半部分是一串提示符,会显示在运行结果中的"文件类型"中,后半部分是正则表达式,用于过滤以.log结尾的所有文件。

JS

filePath = crt.Dialog.FileOpenDialog("请选择一个文件", "Open", "a.log", "Log Files (*.log)|*.log") 

运行结果
在这里插入图片描述

MessageBox

解释
弹出一个消息框,可以定义按钮,使用按钮和文本消息来实现和用户的简单对话。

语法

crt.Dialog.MessageBox(message [, title [icon|buttons]])

参数
(1)message:消息文字,必选参数,见运行结果中的消息正文。
(2)title:弹窗的标题描述,见运行结果中的标题处。
(3)icon:警示图标,见结果中的图1到图4。icon的取值有:16(叉号,表示错误),32(问号,表示确认),48(叹号,表示警告),64(提示,表示信息提示)
(4)buttons:按钮类型,定义不同的类型,可以有不同的选项,同时鼠标点击不同的选项时也有不同的返回值。button取值范围为0到6,
–0:见图5,点击后返回值为1;
–1:见图6,点击’确定’时,返回1,点击’取消’时返回2;
–2:见图7,点击’终止’时,返回3,点击’重试’时返回4,点击’忽略’时返回5;
–3:见图8,点击’是’时,返回6,点击’否’时,返回7,点击’取消’时返回2;
–4:见图9,点击’是’时,返回6,点击’否’时,返回7;
–5:见图10,点击’重试’时,返回4,点击’取消’时返回2;
–6:见图11,点击’取消’时,返回2,点击’重试’时,返回10,点击’继续’,返回11;

JS

crt.Dialog.MessageBox("这里是消息框正文","这里是标题",16|0) 

运行结果
在这里插入图片描述
图1:icon=16

在这里插入图片描述
图2:icon=32

在这里插入图片描述
图3:icon=48

在这里插入图片描述
图4:icon=64

在这里插入图片描述
图5:button=0

在这里插入图片描述
图6:button=1

在这里插入图片描述
图7:button=2

在这里插入图片描述
图8:button=3

在这里插入图片描述
图9:button=4

在这里插入图片描述
图10:button=5

在这里插入图片描述
图11:button=6

Prompt

解释
弹出一个输入框,用户可以填写文字,比如填写文件名,填写路径,填写IP地址等。

语法

crt.Dialog.Prompt(message [, title [, default [, isPassword ]]])

参数
(1)message:消息文字,必选参数,见运行结果中的消息正文。
(2)title:弹窗的标题描述,见运行结果中的标题处。
(3)default:输入框中的默认值,如果为"",则没有默认值。
(4)isPassword:是否要隐藏输入的文字,类似于日常输入密码时显示****
(5)运行结果如下图1,如果点击’ok’,返回输入的字符串,否则返回""

JS

password = crt.Dialog.Prompt("这里是正文","这里是弹窗标题","这是默认值",true) 

运行结果
在这里插入图片描述

Screen

介绍

定义Screen(屏幕)对象,该对象是整个脚本中的灵魂,包含数据抓取、数据发送、等待数据等等重要功能都是有该对象的子属性和方法实现的。

JS

var scrObj = crt.Screen;

Screen的属性

CurrentColumn

解释
返回当前光标处的列坐标。

语法

crt.Screen.CurrentColumn

JS

var curCol = crt.Screen.CurrentColumn;
crt.Dialog.MessageBox(curCol);
CurrentRow

解释
返回当前光标处的行坐标。但这个功能有个问题就是当满屏输出后这个值一直表示最大值,等同于底下要讲的子属性四:Rows的值。

语法

crt.Screen.CurrentRow

JS

var curRow = crt.Screen.CurrentRow;
crt.Dialog.MessageBox(curRow);
Columns

解释
返回当前屏幕的最大列宽。

语法

crt.Screen.Columns

JS

var cols = crt.Screen.Columns;
crt.Dialog.MessageBox(cols);
Rows

解释
返回当前屏幕的最大行宽,这个行宽指的是可见区的,并不是指缓冲区的行宽。

语法

crt.Screen.Rows

JS

var rows = crt.Screen.Rows;
crt.Dialog.MessageBox(rows);
IgnoreEscape

解释
定义当使用WaitForString、WaitForStrings 和 ReadString这三个方法时是否获取Escape字符(也就是特殊控制字符,如"\n"),默认是会获取的。

语法

crt.Screen.IgnoreEscape [ = True | False ]

参数
true|false:当设置为true时不会获取特殊字符,为false时会获取,默认为false。

JS

# $language = "JScript"
# $interface = "1.0"

// 设置false,获取ctrl+c
crt.Screen.IgnoreEscape = false;
crt.Dialog.MessageBox(crt.Screen.ReadString("\03"));

// 设置true,不获取ctrl+c
crt.Screen.IgnoreEscape = true;
crt.Dialog.MessageBox(crt.Screen.ReadString("\03"));
MatchIndex

解释
当使用WaitForStrings 和 ReadString这两个方法获取字符串时,会根据参数的位置获取返回值,而MatchIndex就是这个位置,从1开始计算,如果没有一个匹配到则返回0,可以根据下面的例子来看。

语法
crt.Screen.MatchIndex

JS

# $language = "JScript"
# $interface = "1.0"

var outPut = crt.Screen.ReadString("error", "warning", "#", 10);
switch (crt.Screen.MatchIndex) {
    case 0:
        crt.Dialog.MessageBox("Timed out!")
        break;
    case 1:
        crt.Dialog.MessageBox("Found 'error'")
        break;
    case 2:
        crt.Dialog.MessageBox("Found 'warning'")
        break;
    case 3:
        crt.Dialog.MessageBox("Found '#'")
        break;
}
Synchronous

解释
设置屏幕的同步属性,关于该属性需要谨慎对待,若设置为false,则在脚本中使用WaitForString、WaitForStrings或ReadString函数时可能存在丢失一部分数据的现象,而设置为true时不会,但是设置为true后可能会存在屏幕卡顿的情况,出现这两种现象的原因应该是和这几个函数以及打印字符到Screen的机制有关系,具体内部原因不明,就具体使用而言,如果是跑脚本的话最好还是不要设置为true,大量的卡顿看着就会蛋疼了,还可能会造成CRT卡死。

语法

crt.Screen.Synchronous [ = True | False ]

参数
true|false :默认为false

JS

# $language = "JScript"
# $interface = "1.0"

crt.screen.Synchronous = true;
crt.screen.Send("\r\n");
crt.screen.ReadString("#");
crt.screen.Send("\r\n");
crt.screen.WaitForString("#");
crt.screen.Send("\r\n");
crt.screen.WaitForStrings("#",">");

Screen的方法

Clear

解释
清屏功能

语法

crt.Screen.Clear()

JS

crt.Screen.Send("\r\n");
crt.Screen.Clear();
Get

解释
按照坐标取出一个矩形框内的屏幕上的字符(即从某行某列开始到其它行其它列),不包含字符串中的回车换行符,所以这个多用于获取无格式的光标处字符串或某小段特定区域字符串。

语法

crt.Screen.Get(row1, col1, row2, col2)

JS

var getScr = crt.Screen.Get(31,50,31,56);
crt.Dialog.MessageBox(getScr);

这段代码获取的是(31,50,31,56)这个矩阵内的字符串,即下图中红色框中所示的7483052。
在这里插入图片描述

Get2

解释
按照坐标取出一个矩形框内的屏幕上的字符(即从某行某列开始到其它行其它列),包含字符串中的回车换行符,所以这个多用于获取大段带格式的字符串。

语法

crt.Screen.Get2(row1, col1, row2, col2)

JS

var getScr = crt.Screen.Get2(29,1,35,20);
crt.Dialog.MessageBox(getScr);

这段代码获取的是(29,1,35,20)这个矩阵内的所有带回车换行的字符串,即下图中红色框中文字。
在这里插入图片描述

IgnoreCase

解释
使用全局参数设置控制在使用WaitForString、WaitForStrings和ReadString这三个函数时是否对大小写敏感,默认为false指大小写敏感即大小写字符串都会检查,设置为true时则不会检测大小写。

语法

crt.Screen.IgnoreCase

请注意,语法中并没有带(),根据语法来看,这个不像是方法,但是在CRT的使用说明中标注为了方法而不是属性,且在python的脚本中是当做方法使用的,因此这里先归类为方法,但是在JS脚本中是不带()使用的。

JS

# $language = "JScript"
# $interface = "1.0"

crt.Screen.IgnoreCase = true;
crt.Screen.Send("show memory\r\n");
crt.Screen.WaitForString("more");
crt.Screen.Send("\r\n");
crt.Screen.WaitForStrings("more","#");
crt.Screen.Send("\r\n");
crt.Screen.ReadString("more","#");
Send

解释
向远端设备或者向屏幕(向屏幕发送的功能是CRT7.2以后版本才提供的)发送字符串。如语法中所表示的,string是待发送的字符串,这个字符串可以包含转码字符比如"\r","\n","\03"(ctrl+c),当向屏幕上发送字符串时需要指定第二个参数为true。有了向屏幕发送字符串的功能,我们就可以很方便的和用户进行交互了。可以打印出一些我们需要的报错信息之类的。

语法

crt.Screen.Send(string, [, bSendToScreenOnly])

JS

# $language = "JScript"
# $interface = "1.0"

// 向远程设备发送英文命令"show memory"
crt.Screen.Send("show memory\r\n");

// 向屏幕上发送字符串
crt.Screen.Send("hello,world!\r\n",true);

可是运行了这段代码后很可能会发现,底下的向屏幕发送的字符串先打印到屏幕上了,效果如下,原因是Crt发送到屏幕的字符串可能先于远程设备返回的字符串,因此会先打印下面的。解决办法就是可以在发送屏幕的这一段前面加一个延时,这样就不会先打印下面的了。
在这里插入图片描述

SendKeys

解释
向当前窗口发送按键,包含组合按键,比如可以发送类似"CTRL+ALT+C"等这样的组合键,这样写即可:

crt.screen.sendkeys("^%c");

这个功能需要语言本身支持,目前只有VBS和JS脚本可以使用,Python和Perl都不可以。

语法

crt.Screen.SendKeys(string)

JS

# $language = "JScript"
# $interface = "1.0"

crt.Screen.Clear();
crt.screen.sendkeys("mc~");
crt.Sleep(2000);
crt.screen.sendkeys("{f1}");
crt.Sleep(2000);
crt.screen.sendkeys("{esc}0");
crt.Sleep(2000);
crt.screen.sendkeys("{esc}0");
crt.Sleep(2000);
crt.screen.sendkeys("y");

具体可以有哪些按键,参照下表,修改对应参数即可,可以根据需要自由组合:

Key(按键)Argument(参数)
SHIFT+
CTRL^
ALT%
BACKSPACE{BACKSPACE}, {BS}, or {BKSP}
BREAK{BREAK}
CAPS LOCK{CAPSLOCK}
DEL or DELETE{DELETE} or {DEL}
DOWN ARROW{DOWN}
END{END}
ENTER{ENTER} or ~
ESC{ESC}
HELP{HELP}
HOME{HOME}
INS or INSERT{INSERT} or {INS}
LEFT ARROW{LEFT}
NUM LOCK{NUMLOCK}
PAGE DOWN{PGDN}
PAGE UP{PGUP}
PRINT SCREEN{PRTSC}
RIGHT ARROW{RIGHT}
SCROLL LOCK{SCROLLLOCK}
TAB{TAB}
UP ARROW{UP}
F1, F2, … F16{F1}, {F2}, … {F16}
0, 1, … 9 on number pad{NUM_0}, {NUM_1}, … {NUM_9}
. on number pad{NUM_.}
/ on number pad{NUM_/}
* on number pad{NUM_*}
- on number pad{NUM_-}
+ on number pad{NUM_+}
ENTER on number pad{NUM_ENTER}
HOME on number pad{NUM_HOME}
PAGE UP on number pad{NUM_PGUP}
END on number pad{NUM_END}
PAGE DOWN on number pad{NUM_PGDN}
UP ARROW on number pad{NUM_UP}
DOWN ARROW on number pad{NUM_DOWN}
LEFT ARROW on number pad{NUM_LEFT}
RIGHT ARROW on number pad{NUM_RIGHT}
SendSpecial

解释
可以发送特殊控制码,这个控制码是Crt内置的功能,具体可以包含的有Menu、Telnet、VT functions功能列表中提供的所有功能,即下图中的这些,有兴趣可以调出new button后看到这些:

Menu Function:
在这里插入图片描述
Telnet:
在这里插入图片描述
VT functions:
在这里插入图片描述
语法

crt.Screen.SendSpecial(string)

JS

# $language = "JScript"
# $interface = "1.0"
// 以下是Crt文档中举的例子,具体还有什么我也没有试过,各位可以自己试试
crt.screen.SendSpecial("MENU_PASTE");
crt.screen.SendSpecial("TN_BREAK");
crt.screen.SendSpecial("VT_PF1");
WaitForCursor

解释
等待光标移动,当移动时返回值为true,当有超时时间参数且超时时返回false,否则会一直等待光标移动。利用这个功能可以用来判断一个命令的输出是否结束,只不过超时时间是以秒为单位的,对于脚本当中,这个时间还是略显久了。

语法

crt.Screen.WaitForCursor([timeout])

JS

# $language = "JScript"
# $interface = "1.0"

// 每5秒内光标有移动时即发送一个命令
while (1) {
    if ( crt.Screen.WaitForCursor(5) ) {
        crt.Screen.Send("show version\r\n");
    }
}
WaitForKey

解释
检测有键盘按键时返回true,当有超时时间参数且超时时返回false,否则会一直等待按键。只可惜这个函数不知道输入的键是什么,否则就可以针对性的判断了,它只能检测到有输入而已。

语法

crt.Screen.WaitForKey([timeout])

JS

# $language = "JScript"
# $interface = "1.0"

// 每5秒内有输入时发送一个命令
while (1) {
    if ( crt.Screen.WaitForKey(5) ) {
        crt.Screen.Send("show version\r\n");
    }
}
WaitForString

解释
一般用于发送命令后等待某字符串,这个字符串只要是屏幕上出现的即可,哪怕是粘贴上去的命令也会同样被检测到,也可以用于等待屏幕的输出打印,不需要先发送命令。不过一般我们使用它来检测的都是发送命令后出现的标识符。

语法

crt.Screen.WaitForString(string,[timeout],[bCaseInsensitive])

参数

1、string,必选参数,等待的字符串,可以是特殊字符比如:\r\n;
2、timeout,可选参数,超时时间,当检测不到对应字符串时会返回false,没有此参数时会一直等待;
3、bCaseInsensitive,可选参数,大小写不敏感,默认值是false,表示将检测字符串的大小写,当为true时不检测大小写。

JS

# $language = "JScript"
# $interface = "1.0"

// 发送命令,并在5秒内获取到对应的字符串后发送一条命令
crt.Screen.Send("\r\n");
if ( crt.Screen.WaitForString("#",5) ) {
    crt.Screen.Send("show version\r\n");
}
crt.Screen.Send("\r\n");
if ( crt.Screen.WaitForString(">",5) ) {
    crt.Screen.Send("who\r\n");
}
crt.Screen.Send("\r\n");
if ( crt.Screen.WaitForString("$",5) ) {
    crt.Screen.Send("ls -l\r\n");
}
WaitForStrings

解释
和WaitForString是同样的功能,只不过可以等待多个字符串,如果有匹配到某个字符串,则返回值该字符串在这些字符串中的位置,位置值从1开始。若在超时时间内没有匹配到则返回false,没有超时时间时会一直等待。

语法

crt.Screen.WaitForStrings([string1,string2..],[timeout],[bCaseInsensitive])

参数
1、string,必选参数,等待的字符串,最少有一个,可以是特殊字符比如:\r\n;
2、timeout,可选参数,超时时间,当检测不到对应字符串时会返回false,没有此参数时会一直等待;
3、bCaseInsensitive,可选参数,大小写不敏感,默认值是false,表示将检测字符串的大小写,当为true时不检测大小写。

JS

# $language = "JScript"
# $interface = "1.0"

var outPut = crt.Screen.WaitForStrings("error", "warning", "#", 10);
var waitIndex = crt.Screen.MatchIndex
switch (waitIndex) {
    case 0:
        crt.Dialog.MessageBox("Timed out!")
        break;
    case 1:
        crt.Dialog.MessageBox("Found 'error'")
        break;
    case 2:
        crt.Dialog.MessageBox("Found 'warning'")
        break;
    case 3:
        crt.Dialog.MessageBox("Found '#'")
        break;
}
ReadString

解释
这个功能和上面的WaitForStrings函数有些类似,都是等待某几个字符串出现,不过不同的是,ReadString函数还会读取字符串之前的所有出现的字符,这个功能可以很方便的用于发送一个命令后获取这个命令的输出结果,不过这个函数也不是特别稳定,因为很可能存在命令的输出结果很快,而屏幕又没有捕捉到时,要么会由于超时而返回false,要么会一直等待,最终返回的都是空值,因此完全依靠该函数获取命令的输出的话并不是很把稳(如果程序对于稳定性要求很高的话,那么最好还是不要依赖这个函数。)

语法

crt.Screen.ReadString([string1,string2..],[timeout],[bCaseInsensitive])

参数
1、string,必选参数,等待的字符串,最少有一个,可以是特殊字符比如:\r\n;
2、timeout,可选参数,超时时间,当检测不到对应字符串时会返回false,没有此参数时会一直等待;
3、bCaseInsensitive,可选参数,大小写不敏感,默认值是false,表示将检测字符串的大小写,当为true时不检测大小写。

JS

# $language = "JScript"
# $interface = "1.0"

// 发送命令并根据提示符获取命令的输出。
crt.Screen.Send("show version\r\n");
crt.Screen.WaitForString("show version",2);
var outPut = crt.Screen.ReadString("error", "warning", "#", 10);
var waitIndex = crt.Screen.MatchIndex
switch (waitIndex) {
    case 0:
        crt.Dialog.MessageBox("Timed out!")
        break;
    case 1:
        crt.Dialog.MessageBox("Found 'error'")
        break;
    case 2:
        crt.Dialog.MessageBox("Found 'warning'")
        break;
    case 3:
        crt.Dialog.MessageBox("命令的输出时:"+outPut);
        break;
}

在这个举例中的第6行可以看到还使用了WaitForString,为什么要使用这个,有两个方面原因,一是确定命令已经被发送到了远端设备,二是确保命令的输出结果中没有改命令,而仅仅是该命令的输出结果。

ScriptFullName

该对象返回当前执行脚本的全名,其中包含脚本路径和脚本名,可以使用中文路径和文件名。
JS

var scrName = crt.ScriptFullName;

Session

定义当前会话属性,该属性有许多很实用的属性和方法可以使用。
JS

var sessionObj = crt.Session;

Window

返回当前程序的窗口对象,该对象下有子对象,可以实现诸如隐藏当前窗口等的功能。
JS

var crtWin = crt.Window;

Version

返回当前Crt的软件版本,通常该属性用于比较版本新旧,以处理在不同版本上不能兼容的问题。
JS

// 检查Crt版本,如果低于7.3,则退出Crt。 
crtVer = crt.Version; 
if ( crtVer < 7.3 ) { 
	crt.Quit(); 
}

crt的方法

GetActiveTab()

Crt软件的管理是多Tab的,该方法返回当前活跃状态的Tab界面,该界面作为一个新的Tab属性使用,而Tab属性本身又有许多子属性和方法,因此该方法也是比较实用的一个函数。
JS

var actTab = crt.GetActiveTab();

python

Currenttab = crt.GetActiveTab()

GetScriptTab()

获取当前脚本执行的Tab对象,该对象有一些子对象和方法,通过获取Tab页面,可以轻松的实现在多个Tab中同时发送数据的功能。
JS

// 获取当前脚本执行的Tab,并获取编号(Index从1开始) 
var scriptTab = crt.GetScriptTab(); 
var tabNum = scriptTab.Index; 
// 在当前Tab上发送数据
scriptTab.Screen.Send("脚本执行的Tab是:"+tabNum); 

GetTabCount()

获取当前总Tab数目,是一个数值类型返回值。
JS

// 获取总Tab数目 
var tabCount = crt.GetTabCount();

GetTab()

获取某个位置的Tab对象,该位置即为tab的Index,从1开始,即从当前Crt窗口的最左侧Tab数起。

JS

// 获取指定编号的Tab对象 
var tabCount = crt.GetTabCount(); 
var gTab = ""; 
// 从Tab1到所有Tab依次发送数据 
for ( var i = 1 ; i <= tabCount; i++ ) { 
    gTab = crt.GetTab(i); 
    gTab.Screen.Send("当前Tab为:"+i); 
}

Sleep()

脚本暂停运行时间,millisecond:毫秒

JS

// 获取指定编号的Tab对象 
var tabCount = crt.GetTabCount(); 
var gTab = ""; 
// 从Tab1到所有Tab依次发送数据 
for ( var i = 1 ; i <= tabCount; i++ ) { 
	gTab = crt.GetTab(i); 
	// 暂停5秒再发送数据 
	crt.Sleep(5000); 
	gTab.Screen.Send("当前Tab为:"+i); 
}

python

crt.Sleep(1000)

Quit()

退出Crt软件。(不是退出脚本,是直接退出软件。)

JS

// 如果直接使用crt.Quit的话只要有会话是激活的,那么就会有弹窗出来,因此,可以使用for循环,先将所有Tab断连,再退出。

// 断连所有Tab
var tabCount = crt.GetTabCount();
var gTab = "";
for ( var i = 1 ; i <= tabCount; i++ ) {
    gTab = crt.GetTab(i);
    gTab.Session.Disconnect();
}
// 此时可以直接退出Crt
crt.Quit()

例子1(python)

# $language = "python"
# $interface = "1.0"
#此方法表示你必须先登录一台服务器然后再去telnet到另外一台服务器
#主机的ip
host = '11.1.1.1'
#主机的用户名
user = 'root'
#主机的密码
passwd = 'password'

def main():
    #向屏幕光标后发送以下文字内容,\r表示回车执行
    crt.Screen.Send('telnet '+host+'\r')
    crt.Screen.WaitForString('login:')  
    crt.Screen.Send(user+"\r")
    crt.Screen.WaitForString('password:') 
    crt.Screen.Send(passwd+"\r")
    #使用默认弹窗提示信息
    crt.Dialog.MessageBox('登录成功!')

main()

例子2(JS)

# $language="JScript"
# $interface="1.0"
#主机的ip
host = '11.1.1.1'
#主机的用户名
user = 'root'
#主机的密码
passwd = 'password'
crt.screen.Send("telnet "+host+"\n");
crt.screen.WaitForString("login:");
crt.screen.Send(root+"\n");
crt.screen.WaitForString("password:");
crt.screen.Send(passwd+"\n");
#使用默认弹窗提示信息
crt.Dialog.MessageBox('登录成功!')

官方例子(python)

链接https://www.vandyke.com/support/securecrt/python_examples.html

官方例子(VBScript, JScript and PerlScript)

https://www.vandyke.com/support/securecrt/scripting_examples.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值