Просмотр исходного кода

Merge pull request #2132 from openNAMU/dev

버그 수정 및 최근 변경 임시 변경
잉여개발기 2 лет назад
Родитель
Сommit
2d25076d50

+ 6 - 16
app.py

@@ -318,17 +318,6 @@ def do_every_day():
             
             curs.execute(db_change("delete from re_admin where time < ?"), [time_calc])
 
-        # 전체 문서 수 재계산
-        curs.execute(db_change("select count(*) from data"))
-        count_data = curs.fetchall()
-        if count_data:
-            count_data = count_data[0][0]
-        else:
-            count_data = 0
-
-        curs.execute(db_change('delete from other where name = "count_all_title"'))
-        curs.execute(db_change('insert into other (name, data, coverage) values ("count_all_title", ?, "")'), [str(count_data)])
-
         # 사이트맵 생성 관리
         curs.execute(db_change('select data from other where name = "sitemap_auto_make"'))
         db_data = curs.fetchall()
@@ -485,8 +474,8 @@ app.route('/block_log/ongoing', defaults = { 'tool' : 'ongoing' })(recent_block)
 app.route('/block_log/ongoing/<int:num>', defaults = { 'tool' : 'ongoing' })(recent_block)
 
 # Func-history
-app.route('/recent_change', defaults = { 'tool' : 'recent' })(recent_change)
-app.route('/recent_changes', defaults = { 'tool' : 'recent' })(recent_change)
+app.route('/recent_change')(list_recent_change)
+app.route('/recent_changes')(list_recent_change)
 app.route('/recent_change/<int:num>/<set_type>', defaults = { 'tool' : 'recent' })(recent_change)
 
 app.route('/recent_edit_request', defaults = { 'db_set' : db_set_str })(recent_edit_request)
@@ -516,8 +505,8 @@ app.route('/xref_page/<int:num>/<everything:name>')(view_xref)
 app.route('/xref_this/<everything:name>', defaults = { 'xref_type' : 2 })(view_xref)
 app.route('/xref_this_page/<int:num>/<everything:name>', defaults = { 'xref_type' : 2 })(view_xref)
 
-app.route('/doc_watch_list/<int:num>/<everything:name>', defaults = { 'db_set' : db_set_str, 'do_type' : 'watch_list' })(w_watch_list)
-app.route('/doc_star_doc/<int:num>/<everything:name>', defaults = { 'db_set' : db_set_str, 'do_type' : 'star_doc' })(w_watch_list)
+app.route('/doc_watch_list/<int:num>/<everything:name>')(w_watch_list)
+app.route('/doc_star_doc/<int:num>/<everything:name>', defaults = { 'do_type' : 'star_doc' })(w_watch_list)
 
 app.route('/raw/<everything:name>')(view_w_raw)
 app.route('/raw_acl/<everything:name>', defaults = { 'doc_acl' : 'on' })(view_w_raw)
@@ -728,7 +717,8 @@ app.route('/api/recent_discuss/<int:num>')(api_recent_discuss)
 app.route('/api/recent_discuss')(api_recent_discuss)
 ##
 
-app.route('/api/lang/<data>')(api_func_lang)
+app.route('/api/lang', methods = ['POST'], defaults = { 'db_set' : db_set_str })(api_func_language)
+app.route('/api/lang/<data>', defaults = { 'db_set' : db_set_str })(api_func_language)
 app.route('/api/sha224/<everything:data>')(api_func_sha224)
 app.route('/api/ip/<everything:data>', defaults = { 'db_set' : db_set_str })(api_func_ip)
 

+ 4 - 1
route/__init__.py

@@ -1,4 +1,3 @@
-from route.api_func_lang import api_func_lang
 from route.api_image_view import api_image_view
 from route.api_recent_discuss import api_recent_discuss
 from route.api_setting import api_setting
@@ -167,8 +166,12 @@ from route.vote_end import vote_end
 from route.vote_list import vote_list
 from route.vote_select import vote_select
 
+from route.n_list_recent_change import list_recent_change
+
 from route.n_w_watch_list import w_watch_list
 
+from route.go_api_func_llm import api_func_llm
+from route.go_api_func_language import api_func_language
 from route.go_api_func_sha224 import api_func_sha224
 from route.go_api_func_ip import api_func_ip
 

+ 0 - 5
route/api_func_lang.py

@@ -1,5 +0,0 @@
-from .tool.func import *
-
-def api_func_lang(data = 'Test'):
-    with get_db_connect() as conn:
-        return flask.jsonify({ "data" : get_lang(conn, data) })

+ 2 - 0
route/edit.py

@@ -111,6 +111,8 @@ def edit(name = 'Test', section = 0, do_type = ''):
         edit_req_mode = 0
         if acl_check(conn, name, 'document_edit') == 1:
             edit_req_mode = 1
+            if acl_check(name, 'document_edit_request') == 1:
+                return redirect('/raw_acl/' + url_pas(name))
             
         if do_title_length_check(conn, name) == 1:
             return re_error(conn, '/error/38')

+ 1 - 1
route/edit_move.py

@@ -230,8 +230,8 @@ def edit_move(name):
                         
                         <h2>''' + get_lang(conn, 'document') + '''</h2>
                         <select name="move_option">
-                            <option value="none"> ''' + get_lang(conn, 'dont_move') + '''</option>
                             <option value="normal"> ''' + get_lang(conn, 'normal') + '''</option>
+                            <option value="none"> ''' + get_lang(conn, 'dont_move') + '''</option>
                             <option value="reverse"> ''' + get_lang(conn, 'replace_move') + '''</option>
                             ''' + ('<option value="merge"> ' + get_lang(conn, 'merge_move') + '</option>' if owner_auth == 1 else '') + '''
                         </select>

+ 27 - 0
route/go_api_func_language.py

@@ -0,0 +1,27 @@
+from .tool.func import *
+
+def api_func_language(db_set, data = 'Test'):
+    with get_db_connect() as conn:
+        other_set = {}
+        if flask.request.method == 'POST':
+            other_set["data"] = flask.request.form.get('data', '')
+            other_set["data"] = other_set["data"].split(' ')
+        else:
+            other_set["data"] = [data]
+        
+        other_set = json.dumps(other_set)
+
+        if platform.system() == 'Linux':
+            if platform.machine() in ["AMD64", "x86_64"]:
+                data = subprocess.Popen([os.path.join(".", "route_go", "bin", "main.amd64.bin"), sys._getframe().f_code.co_name, db_set, other_set], stdout = subprocess.PIPE).communicate()[0]
+            else:
+                data = subprocess.Popen([os.path.join(".", "route_go", "bin", "main.arm64.bin"), sys._getframe().f_code.co_name, db_set, other_set], stdout = subprocess.PIPE).communicate()[0]
+        else:
+            if platform.machine() in ["AMD64", "x86_64"]:
+                data = subprocess.Popen([os.path.join(".", "route_go", "bin", "main.amd64.exe"), sys._getframe().f_code.co_name, db_set, other_set], stdout = subprocess.PIPE).communicate()[0]
+            else:
+                data = subprocess.Popen([os.path.join(".", "route_go", "bin", "main.arm64.exe"), sys._getframe().f_code.co_name, db_set, other_set], stdout = subprocess.PIPE).communicate()[0]
+
+        data = data.decode('utf8')
+
+        return flask.Response(response = data, status = 200, mimetype = 'application/json')

+ 26 - 0
route/go_api_func_llm.py

@@ -0,0 +1,26 @@
+from .tool.func import *
+
+def api_func_llm(db_set):
+    with get_db_connect() as conn:
+        if flask.request.method == 'POST':
+            other_set = {}
+            other_set["prompt"] = flask.request.form.get('prompt', '')
+            other_set["ip"] = ip_check()
+            other_set = json.dumps(other_set)
+
+            if platform.system() == 'Linux':
+                if platform.machine() in ["AMD64", "x86_64"]:
+                    data = subprocess.Popen([os.path.join(".", "route_go", "bin", "main.amd64.bin"), sys._getframe().f_code.co_name, db_set, other_set], stdout = subprocess.PIPE).communicate()[0]
+                else:
+                    data = subprocess.Popen([os.path.join(".", "route_go", "bin", "main.arm64.bin"), sys._getframe().f_code.co_name, db_set, other_set], stdout = subprocess.PIPE).communicate()[0]
+            else:
+                if platform.machine() in ["AMD64", "x86_64"]:
+                    data = subprocess.Popen([os.path.join(".", "route_go", "bin", "main.amd64.exe"), sys._getframe().f_code.co_name, db_set, other_set], stdout = subprocess.PIPE).communicate()[0]
+                else:
+                    data = subprocess.Popen([os.path.join(".", "route_go", "bin", "main.arm64.exe"), sys._getframe().f_code.co_name, db_set, other_set], stdout = subprocess.PIPE).communicate()[0]
+
+            data = data.decode('utf8')
+
+            return flask.Response(response = data, status = 200, mimetype = 'application/json')
+        else:
+            return flask.jsonify({})

+ 0 - 0
route/n_list_history.py


+ 13 - 0
route/n_list_recent_change.py

@@ -0,0 +1,13 @@
+from .tool.func import *
+
+def list_recent_change():
+    with get_db_connect() as conn:
+        return easy_minify(conn, flask.render_template(skin_check(conn),
+            imp = [get_lang(conn, 'recent_change'), wiki_set(conn), wiki_custom(conn), wiki_css([0, 0])],
+            data = '' + \
+                '<div id="opennamu_list_recent_change"></div>' + \
+                '<script src="/views/main_css/js/route/list_recent_change.js' + cache_v() + '"></script>' + \
+                '<script>opennamu_list_recent_change();</script>' + \
+            '',
+            menu = [['other', get_lang(conn, 'return')]]
+        ))

+ 13 - 0
route/n_list_recent_discuss.py

@@ -0,0 +1,13 @@
+from .tool.func import *
+
+def list_recent_discuss():
+    with get_db_connect() as conn:
+        return easy_minify(conn, flask.render_template(skin_check(conn),
+            imp = [get_lang(conn, 'recent_discuss'), wiki_set(conn), wiki_custom(conn), wiki_css([0, 0])],
+            data = '' + \
+                '<div id="opennamu_list_recent_discuss"></div>' + \
+                '<script src="/views/main_css/js/route/list_recent_discuss.js' + cache_v() + '"></script>' + \
+                '<script>opennamu_list_recent_discuss();</script>' + \
+            '',
+            menu = [['other', get_lang(conn, 'return')]]
+        ))

+ 6 - 15
route/n_w_watch_list.py

@@ -1,22 +1,13 @@
 from .tool.func import *
 
-from .go_api_w_watch_list import api_w_watch_list
-
-def w_watch_list(db_set, name, num = 1, do_type = 'watch_list'):
+def w_watch_list(name, num = 1, do_type = 'watch_list'):
     with get_db_connect() as conn:
-        data = '<a href="/doc_watch_list/1/' + url_pas(name) + '">(' + get_lang(conn, 'watchlist') + ')</a> <a href="/doc_star_doc/1/' + url_pas(name) + '">(' + get_lang(conn, 'star_doc') + ')</a>'
-        data += '<ul class="opennamu_ul">'
-        
-        list_data = json.loads(api_w_watch_list(db_set, name, do_type, num).data)
-        data += ''.join(['<li><span class="opennamu_render_ip">' + for_a + '</span></li>' for for_a in list_data])
-
-        data += '</ul>'
-        data += '<script>opennamu_do_ip_render();</script>'
-
-        data += get_next_page_bottom(conn, '/doc_' + do_type + '/{}/' + url_pas(name), num, list_data)
-
         return easy_minify(conn, flask.render_template(skin_check(conn),
             imp = [name, wiki_set(conn), wiki_custom(conn), wiki_css(['(' + get_lang(conn, do_type if do_type == 'star_doc' else 'watchlist') + ')', 0])],
-            data = data,
+            data = '' + \
+                '<div id="opennamu_w_watch_list"></div>' + \
+                '<script src="/views/main_css/js/route/w_watch_list.js' + cache_v() + '"></script>' + \
+                '<script>opennamu_w_watch_list(' + str(num) + ');</script>' + \
+            '',
             menu = [['w/' + url_pas(name), get_lang(conn, 'return')]]
         ))

+ 10 - 1
route/tool/func.py

@@ -1043,7 +1043,7 @@ def skin_check(conn, set_n = 0):
         return skin
     
 def cache_v():
-    return '.cache_v219'
+    return '.cache_v221'
 
 def wiki_css(data):
     global global_wiki_set
@@ -2509,10 +2509,19 @@ def history_plus(conn, title, data, date, ip, send, leng, t_check = '', mode = '
 
     if mode != 'add' and mode != 'setting' and mode != 'user':
         history_plus_rc_max(conn, 'normal')
+
         curs.execute(db_change("insert into rc (id, title, date, type) values (?, ?, ?, 'normal')"), [id_data, title, date])
     
     if mode != 'add' and mode != 'setting':
         history_plus_rc_max(conn, mode)
+
+        curs.execute(db_change("select count(*) from data"))
+        count_data = curs.fetchall()
+        count_data = count_data[0][0] if count_data else 0
+
+        curs.execute(db_change('delete from other where name = "count_all_title"'))
+        curs.execute(db_change('insert into other (name, data, coverage) values ("count_all_title", ?, "")'), [str(count_data)])
+
         curs.execute(db_change("insert into rc (id, title, date, type) values (?, ?, ?, ?)"), [id_data, title, date, mode])
 
         data_set_exist = '' if mode != 'delete' else 'not_exist'

+ 1 - 1
route/view_w.py

@@ -150,7 +150,7 @@ def view_w(name = 'Test', do_type = ''):
                     <img src="/image/''' + url_pas(file_all_name) + '''.cache_v''' + rev + '''">
                     <h2>''' + get_lang(conn, 'data') + '''</h2>
                     <table>
-                        <tr><td>URL</td><td><a href="/image/''' + url_pas(file_all_name) + '''">''' + get_lang(conn, 'link') + '''</a></td></tr>
+                        <tr><td>''' + get_lang(conn, 'url') + '''</td><td><a href="/image/''' + url_pas(file_all_name) + '''">''' + get_lang(conn, 'link') + '''</a></td></tr>
                         <tr><td>''' + get_lang(conn, 'volume') + '''</td><td>''' + file_size + '''KB</td></tr>
                         <tr><td>''' + get_lang(conn, 'resolution') + '''</td><td>''' + file_res + '''</td></tr>
                     </table>

BIN
route_go/bin/main.amd64.bin


BIN
route_go/bin/main.amd64.exe


BIN
route_go/bin/main.arm64.bin


BIN
route_go/bin/main.arm64.exe


+ 24 - 1
route_go/go.mod

@@ -13,10 +13,21 @@ require (
 )
 
 require (
+	cloud.google.com/go v0.110.8 // indirect
+	cloud.google.com/go/ai v0.3.0 // indirect
+	cloud.google.com/go/compute v1.23.1 // indirect
+	cloud.google.com/go/compute/metadata v0.2.3 // indirect
+	cloud.google.com/go/longrunning v0.5.2 // indirect
 	filippo.io/edwards25519 v1.1.0 // indirect
 	github.com/aymerick/douceur v0.2.0 // indirect
 	github.com/dustin/go-humanize v1.0.1 // indirect
+	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
+	github.com/golang/protobuf v1.5.3 // indirect
+	github.com/google/generative-ai-go v0.10.0 // indirect
+	github.com/google/s2a-go v0.1.7 // indirect
 	github.com/google/uuid v1.6.0 // indirect
+	github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
+	github.com/googleapis/gax-go/v2 v2.12.0 // indirect
 	github.com/gorilla/css v1.0.1 // indirect
 	github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
 	github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
@@ -24,11 +35,23 @@ require (
 	github.com/ncruces/go-strftime v0.1.9 // indirect
 	github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
 	github.com/russross/blackfriday v1.6.0 // indirect
+	go.opencensus.io v0.24.0 // indirect
+	golang.org/x/crypto v0.21.0 // indirect
 	golang.org/x/mod v0.16.0 // indirect
 	golang.org/x/net v0.22.0 // indirect
+	golang.org/x/oauth2 v0.13.0 // indirect
+	golang.org/x/sync v0.6.0 // indirect
 	golang.org/x/sys v0.18.0 // indirect
+	golang.org/x/text v0.14.0 // indirect
 	golang.org/x/tools v0.19.0 // indirect
-	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
+	golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
+	google.golang.org/api v0.149.0 // indirect
+	google.golang.org/appengine v1.6.7 // indirect
+	google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect
+	google.golang.org/grpc v1.59.0 // indirect
+	google.golang.org/protobuf v1.33.0 // indirect
 	lukechampine.com/uint128 v1.2.0 // indirect
 	modernc.org/cc/v3 v3.41.0 // indirect
 	modernc.org/ccgo/v3 v3.16.15 // indirect

+ 134 - 0
route_go/go.sum

@@ -1,19 +1,74 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.110.8 h1:tyNdfIxjzaWctIiLYOTalaLKZ17SI44SKFW26QbOhME=
+cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk=
+cloud.google.com/go/ai v0.3.0 h1:M617N0brv+XFch2KToZUhv6ggzgFZMUnmDkNQjW2pYg=
+cloud.google.com/go/ai v0.3.0/go.mod h1:dTuQIBA8Kljuas5z1WNot1QZOl476A9TsFqEi6pzJlI=
+cloud.google.com/go/compute v1.23.1 h1:V97tBoDaZHb6leicZ1G6DLK2BAaZLJ/7+9BB/En3hR0=
+cloud.google.com/go/compute v1.23.1/go.mod h1:CqB3xpmPKKt3OJpW2ndFIXnA9A4xAy/F3Xp1ixncW78=
+cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
+cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
+cloud.google.com/go/longrunning v0.5.2 h1:u+oFqfEwwU7F9dIELigxbe0XVnBAo9wqMuQLA50CZ5k=
+cloud.google.com/go/longrunning v0.5.2/go.mod h1:nqo6DQbNV2pXhGDbDMoN2bWz68MjZUzqv2YttZiveCs=
 filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
 filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
 github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
 github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
 github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
 github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4=
 github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
+github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/google/generative-ai-go v0.10.0 h1:r7LAhVtl+57x70Ub/XmV6T54db8e2sVp9vhRn+RvX3M=
+github.com/google/generative-ai-go v0.10.0/go.mod h1:uxrCJXjAIjJS8rGOU4Ifv1WfOmQYZyEGcMld+cjkd6Q=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
+github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
+github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
+github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
+github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
+github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
 github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
 github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
 github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
@@ -33,6 +88,8 @@ github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02C
 github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
 github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
@@ -40,27 +97,55 @@ github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3V
 github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
 github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA=
 github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
+go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
+go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
+golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
 golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
 golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
 golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
 golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
 golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY=
+golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
+golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -74,8 +159,15 @@ golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
 golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78 h1:M8tBwCtWD/cZV9DZpFYRUgaymAYAr+aIUTWzDaM3uPs=
 golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@@ -85,8 +177,50 @@ golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
 golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
+google.golang.org/api v0.149.0 h1:b2CqT6kG+zqJIVKRQ3ELJVLN1PwHZ6DJ3dW8yl82rgY=
+google.golang.org/api v0.149.0/go.mod h1:Mwn1B7JTXrzXtnvmzQE2BD6bYZQ8DShKZDZbeN9I7qI=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
+google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b h1:+YaDE2r2OG8t/z5qmsh7Y+XXwCbvadxxZ0YY6mTdrVA=
+google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI=
+google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b h1:CIC2YMXmIhYw6evmhPxBKJ4fmLbOFtXQN/GV3XOZR8k=
+google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
+google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
+google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
+google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
 lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
 modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=

+ 6 - 2
route_go/main.go

@@ -23,9 +23,9 @@ func main() {
 	} else if call_arg[0] == "api_func_ip" {
 		route.Api_func_ip(call_arg[1:])
 	} else if call_arg[0] == "api_recent_change" {
-		route.Api_recent_change(call_arg[1:])
+		route.Api_list_recent_change(call_arg[1:])
 	} else if call_arg[0] == "api_recent_edit_request" {
-		route.Api_recent_edit_request(call_arg[1:])
+		route.Api_list_recent_edit_request(call_arg[1:])
 	} else if call_arg[0] == "api_bbs" {
 		route.Api_bbs(call_arg[1:])
 	} else if call_arg[0] == "api_w_xref" {
@@ -36,5 +36,9 @@ func main() {
 		route.Api_user_watch_list(call_arg[1:])
 	} else if call_arg[0] == "api_w_render" {
 		route.Api_w_render(call_arg[1:])
+	} else if call_arg[0] == "api_func_llm" {
+		route.Api_func_llm(call_arg[1:])
+	} else if call_arg[0] == "api_func_language" {
+		route.Api_func_language(call_arg[1:])
 	}
 }

+ 33 - 0
route_go/route/api_func_language.go

@@ -0,0 +1,33 @@
+package route
+
+import (
+	"encoding/json"
+	"fmt"
+	"opennamu/route/tool"
+)
+
+func Api_func_language(call_arg []string) {
+	db_set := map[string]string{}
+	json.Unmarshal([]byte(call_arg[0]), &db_set)
+
+	other_set := make(map[string]interface{})
+	json.Unmarshal([]byte(call_arg[1]), &other_set)
+
+	db := tool.DB_connect(db_set)
+	if db == nil {
+		return
+	}
+	defer db.Close()
+
+	data_list := map[string][]string{}
+	data_list["data"] = []string{}
+
+	temp_list := other_set["data"].([]interface{})
+
+	for for_a := 0; for_a < len(temp_list); for_a++ {
+		data_list["data"] = append(data_list["data"], tool.Get_language(db, db_set, temp_list[for_a].(string), false))
+	}
+
+	json_data, _ := json.Marshal(data_list)
+	fmt.Print(string(json_data))
+}

+ 62 - 0
route_go/route/api_func_llm.go

@@ -0,0 +1,62 @@
+package route
+
+import (
+	"context"
+	"database/sql"
+	"encoding/json"
+	"fmt"
+	"opennamu/route/tool"
+
+	"github.com/google/generative-ai-go/genai"
+	"google.golang.org/api/option"
+)
+
+func Api_func_llm(call_arg []string) {
+	db_set := map[string]string{}
+	json.Unmarshal([]byte(call_arg[0]), &db_set)
+
+	other_set := map[string]string{}
+	json.Unmarshal([]byte(call_arg[1]), &other_set)
+
+	db := tool.DB_connect(db_set)
+	if db == nil {
+		return
+	}
+	defer db.Close()
+
+	var api_key string
+
+	stmt, err := db.Prepare(tool.DB_change(db_set, "select data from user_set where name = 'llm_api_key' and id = ?"))
+	if err != nil {
+		return
+	}
+	defer stmt.Close()
+
+	err = stmt.QueryRow(other_set["ip"]).Scan(api_key)
+	if err != nil {
+		if err == sql.ErrNoRows {
+			api_key = ""
+		} else {
+			return
+		}
+	}
+
+	ctx := context.Background()
+
+	client, err := genai.NewClient(ctx, option.WithAPIKey(api_key))
+	if err != nil {
+		return
+	}
+	defer client.Close()
+
+	model := client.GenerativeModel("gemini-pro")
+	resp, err := model.GenerateContent(ctx, genai.Text(other_set["prompt"]))
+	if err != nil {
+		return
+	}
+
+	text := resp.Candidates[0].Content.Parts[0]
+
+	json_data, _ := json.Marshal(map[string]genai.Part{"data": text})
+	fmt.Print(string(json_data))
+}

+ 1 - 1
route_go/route/api_recent_change.go → route_go/route/api_list_recent_change.go

@@ -8,7 +8,7 @@ import (
 	"strconv"
 )
 
-func Api_recent_change(call_arg []string) {
+func Api_list_recent_change(call_arg []string) {
 	db_set := map[string]string{}
 	json.Unmarshal([]byte(call_arg[0]), &db_set)
 

+ 1 - 1
route_go/route/api_recent_discuss.go → route_go/route/api_list_recent_discuss.go

@@ -7,7 +7,7 @@ import (
 	"strconv"
 )
 
-func Api_recent_discuss(call_arg []string) {
+func Api_list_recent_discuss(call_arg []string) {
 	db_set := map[string]string{}
 	json.Unmarshal([]byte(call_arg[0]), &db_set)
 

+ 1 - 1
route_go/route/api_recent_edit_request.go → route_go/route/api_list_recent_edit_request.go

@@ -8,7 +8,7 @@ import (
 	"strconv"
 )
 
-func Api_recent_edit_request(call_arg []string) {
+func Api_list_recent_edit_request(call_arg []string) {
 	db_set := map[string]string{}
 	json.Unmarshal([]byte(call_arg[0]), &db_set)
 

+ 1 - 1
route_go/route/tool/markdown.go

@@ -150,7 +150,7 @@ func Markdown(db *sql.DB, db_set map[string]string, data map[string]string) map[
 
 	end_data := make(map[string]interface{})
 	end_data["data"] = string_data
-	end_data["js_data"] = ""
+	end_data["js_data"] = "opennamu_do_toc();"
 	end_data["backlink"] = end_backlink
 	end_data["link_count"] = link_count
 

+ 3 - 3
route_go/route/tool/render.go

@@ -80,7 +80,7 @@ func Get_render_direct(db *sql.DB, db_set map[string]string, doc_name string, da
 	} else if markup == "markdown" {
 		render_data = Markdown(db, db_set, doc_data_set)
 	} else {
-		render_data["data"] = "<div id=\"opennamu_render_complete\">" + data + "</div>"
+		render_data["data"] = data
 		render_data["js_data"] = ""
 		render_data["backlink"] = [][]string{}
 	}
@@ -168,7 +168,7 @@ func Get_render_direct(db *sql.DB, db_set map[string]string, doc_name string, da
 	}
 
 	return map[string]string{
-		"data":    render_data["data"].(string),
-		"data_js": render_data["js_data"].(string),
+		"data":    "<div id=\"opennamu_render_complete\">" + render_data["data"].(string) + "</div>",
+		"js_data": render_data["js_data"].(string),
 	}
 }

+ 1 - 1
version.json

@@ -1,6 +1,6 @@
 {
     "beta" : {
-        "r_ver" : "v3.5.0-dev59",
+        "r_ver" : "v3.5.0-dev65",
         "c_ver" : "3500376",
         "s_ver" : "3500113"
     }

+ 22 - 2
views/main_css/js/func/func.js

@@ -84,8 +84,8 @@ function opennamu_do_render(to_obj, data, name = '', do_type = '', option = '')
     }).then(function(text) {
         if(document.getElementById(to_obj)) {
             if(text["data"]) {
-                document.getElementById(to_obj).innerHTML = text.data;
-                eval(text.js_data);
+                document.getElementById(to_obj).innerHTML = text["data"];
+                eval(text["js_data"]);
             } else {
                 document.getElementById(to_obj).innerHTML = '';
             }
@@ -93,6 +93,26 @@ function opennamu_do_render(to_obj, data, name = '', do_type = '', option = '')
     });
 }
 
+function opennamu_page_control(url, page, data_length, data_length_max = 50) {
+    let next = function() {
+        if(data_length_max === data_length) {
+            return '<a href="' + url.replace('{}', String(page + 1)) + '">(-)</a>';
+        } else {
+            return '';
+        }
+    };
+
+    let back = function() {
+        if(page !== 1) {
+            return '<a href="' + url.replace('{}', String(page - 1)) + '">(-)</a>';
+        } else {
+            return '';
+        }
+    };
+
+    return (next() + ' ' + back()).replace(/^ /, '');
+}
+
 function opennamu_xss_filter(str) {
     return str.replace(/[&<>"'\/]/g, function(match) {
         switch(match) {

+ 85 - 0
views/main_css/js/route/list_recent_change.js

@@ -0,0 +1,85 @@
+"use strict";
+
+function opennamu_list_recent_change() {
+    let lang_data = new FormData();
+    lang_data.append('data', 'tool normal edit move delete revert new_doc edit_request user_document')
+
+    fetch('/api/lang', {
+        method : 'post',
+        body : lang_data,
+    }).then(function(res) {
+        return res.json();
+    }).then(function(lang) {
+        lang = lang["data"];
+
+        fetch('/api/recent_change/50').then(function(res) {
+            return res.json();
+        }).then(function(data) {
+            /*
+                data_list = append(data_list, []string{
+                    id,
+                    title,
+                    date,
+                    tool.IP_preprocess(db, db_set, ip, other_set["ip"])[0],
+                    send,
+                    leng,
+                    hide,
+                    tool.IP_parser(db, db_set, ip, other_set["ip"]),
+                    type_data,
+                })
+            */
+
+            let data_html = '';
+
+            let option_list = [
+                ['normal', lang[1]],
+                ['edit', lang[2]],
+                ['move', lang[3]],
+                ['delete', lang[4]],
+                ['revert', lang[5]],
+                ['r1', lang[6]],
+                ['edit_request', lang[7]],
+                ['user', lang[8]]
+            ];
+            for(let for_a = 0; for_a < option_list.length; for_a++) {
+                data_html += '<a href="/recent_change/1/' + option_list[for_a][0] + '">(' + option_list[for_a][1] + ')</a> ';
+            }
+
+            data_html += '<hr class="main_hr">'
+
+            data_html += '<ul class="opennamu_ul">';
+            for(let for_a = 0; for_a < data.length; for_a++) {
+                let doc_name = opennamu_do_url_encode(data[for_a][1]);
+
+                data_html += '<li>';
+                data_html += '<a href="/w/' + doc_name + '">' + data[for_a][1] + '</a> ';
+
+                let rev = Number(data[for_a][0]);
+                if(rev <= 1) {
+                    data_html += '<a href="/history/' + doc_name + '">(r' + data[for_a][0] + ')</a> ';
+                } else {
+                    data_html += '<a href="/diff/' + String(rev - 1) + '/' + data[for_a][0] + '/' + doc_name + '">(r' + data[for_a][0] + ')</a> ';
+                }
+                
+                if(data[for_a][5] === '0') {
+                    data_html += '<span style="color: gray;">(' + data[for_a][5] + ')</span> ';
+                } else if(data[for_a][5].match(/\+/)) {
+                    data_html += '<span style="color: green;">(' + data[for_a][5] + ')</span> ';
+                } else {
+                    data_html += '<span style="color: red;">(' + data[for_a][5] + ')</span> ';
+                }
+                
+                data_html += '<a href="/history_tool/' + data[for_a][0] + '/' + doc_name + '">(' + lang[0] + ')</a> | ';
+                data_html += data[for_a][7] + ' | ';
+                data_html += data[for_a][2];
+                data_html += '<br>'
+                data_html += data[for_a][4];
+                data_html += '</li>';
+            }
+
+            data_html += '</ul>';
+
+            document.getElementById('opennamu_list_recent_change').innerHTML = data_html;
+        });
+    });
+}

+ 44 - 0
views/main_css/js/route/render.js

@@ -267,4 +267,48 @@ function opennamu_do_include(name, render_name, to_obj, option_obj) {
             document.getElementById(option_obj).style.display = "inline";
         }
     });
+}
+
+function opennamu_do_toc() {
+    let data = document.getElementById('opennamu_render_complete');
+    let h_tag = data.querySelectorAll("h1, h2, h3, h4, h5, h6");
+    let toc_count = [0, 0, 0, 0, 0, 0];
+    let toc_html = '';
+
+    for(let for_a = 0; for_a < h_tag.length; for_a++) {
+        let tag = h_tag[for_a].tagName.toLowerCase();
+        tag = tag.replace('h', '');
+        tag = Number(tag) - 1;
+
+        for(let for_b = tag + 1; for_b < 6; for_b++) {
+            toc_count[for_b] = 0;
+        }
+
+        toc_count[tag] += 1;
+
+        let toc_string = '';
+        let add_on = false;
+        for(let for_b = 5; for_b >= 0; for_b--) {
+            if(add_on == false && toc_count[for_b] != 0) {
+                add_on = true;
+            }
+
+            if(add_on == true) {
+                toc_string = String(toc_count[for_b]) + '.' + toc_string;
+            }
+        }
+
+        toc_string = toc_string.replace(/^(0\.)+/, '');
+
+        let toc_string_sub =  toc_string.replace(/\.$/, '');
+        let toc_margin = '<span style="margin-left: 10px;"></span>'.repeat(toc_string_sub.split('.').length - 1);
+
+        toc_html += toc_margin + '<a href="#s-' + toc_string_sub + '">' + toc_string + '</a> ' + h_tag[for_a].innerHTML + '<br>';
+        h_tag[for_a].innerHTML = '<a id="s-' + toc_string_sub + '" href="#toc">' + toc_string + '</a> ' + h_tag[for_a].innerHTML;
+    }
+
+    data.innerHTML = data.innerHTML.replace(/(<h[1-6]>)/, '<div class="opennamu_toc"></div>$1');
+    data.innerHTML = data.innerHTML.replace(/<div class="opennamu_toc"><\/div>/g, function(match) {
+        return '<div class="opennamu_TOC" id="toc"><div class="opennamu_TOC_title">TOC</div><br>' + toc_html + '</div>';
+    });
 }

+ 49 - 0
views/main_css/js/route/w_watch_list.js

@@ -0,0 +1,49 @@
+"use strict";
+
+function opennamu_w_watch_list(page = 1) {
+    let lang_data = new FormData();
+    lang_data.append('data', 'watchlist star_doc');
+
+    fetch('/api/lang', {
+        method : 'POST',
+        body : lang_data
+    }).then(function(res) {
+        return res.json();
+    }).then(function(lang) {
+        lang = lang["data"];
+        let url = window.location.pathname;
+
+        let do_type = url.match('star_doc');
+        if(do_type) {
+            do_type = 'star_doc';
+        } else {
+            do_type = 'watch_list'
+        }
+
+        console.log();
+
+        let split_url = url.split('/');
+        let doc_name = split_url.slice(3, undefined);
+
+        fetch('/api/' + url).then(function(res) {
+            return res.json();
+        }).then(function(data) {
+            let data_html = '<a href="/doc_watch_list/1/' + doc_name + '">(' + lang[0] + ')</a> <a href="/doc_star_doc/1/' + doc_name + '">(' + lang[1] + ')</a>';
+            data_html += '<hr class="main_hr">'
+
+            data_html += '<ul class="opennamu_ul">';
+            for(let for_a = 0; for_a < data.length; for_a++) {
+                data_html += '<li><span class="opennamu_render_ip">' + data[for_a] + '</span></li>';
+            }
+
+            data_html += '</ul>';
+            data_html += '<hr class="main_hr">'
+            
+            data_html += opennamu_page_control('/doc_' + do_type + '/{}/' + doc_name, page, data.length);
+
+            document.getElementById('opennamu_w_watch_list').innerHTML = data_html;
+
+            opennamu_do_ip_render();
+        });
+    });
+}