jugyo-termtter 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  require 'erb'
4
+ require 'set'
4
5
 
5
6
  config.plugins.standard.set_default(
6
7
  :limit_format,
@@ -8,40 +9,61 @@ config.plugins.standard.set_default(
8
9
 
9
10
  module Termtter::Client
10
11
 
11
- # standard commands
12
-
13
12
  register_command(
14
13
  :name => :update, :aliases => [:u],
15
14
  :exec_proc => lambda {|arg|
16
- unless /^\s*$/ =~ arg
17
- # TODO: Change to able to disable erb.
18
- text = ERB.new(arg).result(binding).gsub(/\n/, ' ')
19
- result = Termtter::API.twitter.update(text)
20
- puts "=> #{text}"
21
- result
15
+ unless arg.empty?
16
+ Termtter::API.twitter.update(arg)
17
+ puts "=> #{arg}"
22
18
  end
23
19
  },
24
20
  :completion_proc => lambda {|cmd, args|
25
21
  if /(.*)@([^\s]*)$/ =~ args
26
22
  find_user_candidates $2, "#{cmd} #{$1}@%s"
27
23
  end
28
- }
24
+ },
25
+ :help => ["update,u TEXT", "Post a new message"]
29
26
  )
30
27
 
28
+ direct_message_struct = Struct.new(:id, :text, :user, :created_at)
29
+ direct_message_struct.class_eval do
30
+ def method_missing(*args, &block)
31
+ nil
32
+ end
33
+ end
31
34
  register_command(
32
35
  :name => :direct, :aliases => [:d],
33
36
  :exec_proc => lambda {|arg|
34
- if /^([^\s]+)\s+(.*)\s*$/ =~ arg
37
+ case arg
38
+ when /^([^\s]+)\s+(.*)\s*$/
35
39
  user, text = $1, $2
36
40
  Termtter::API.twitter.direct_message(user, text)
37
41
  puts "=> to:#{user} message:#{text}"
42
+ when 'list'
43
+ output(
44
+ Termtter::API.twitter.direct_messages.map { |d|
45
+ direct_message_struct.new(d.id, "#{d.text} => #{d.recipient_screen_name}", d.sender, d.created_at)
46
+ },
47
+ :direct_messages
48
+ )
49
+ when 'sent_list'
50
+ output(
51
+ Termtter::API.twitter.sent_direct_messages.map { |d|
52
+ direct_message_struct.new(d.id, "#{d.text} => #{d.recipient_screen_name}", d.sender, d.created_at)
53
+ },
54
+ :direct_messages
55
+ )
38
56
  end
39
57
  },
40
- :completion_proc => lambda {|cmd, args|
41
- if /^([^\s]+)$/ =~ args
42
- find_user_candidates $1, "#{cmd} %s"
43
- end
44
- }
58
+ :completion_proc => lambda {|cmd, arg|
59
+ find_user_candidates(arg, "#{cmd} %s") +
60
+ ["list", "sent_list"].grep(/^#{Regexp.quote(arg)}/).map { |i| "#{cmd} #{i}" }
61
+ },
62
+ :help => [
63
+ ["direct,d USERNAME TEXT", "Send direct message"],
64
+ ["direct,d list", 'List direct messages'],
65
+ ["direct,d sent_list", 'List sent direct messages']
66
+ ]
45
67
  )
46
68
 
47
69
  register_command(
@@ -60,7 +82,8 @@ module Termtter::Client
60
82
  },
61
83
  :completion_proc => lambda {|cmd, arg|
62
84
  find_user_candidates arg, "#{cmd} %s"
63
- }
85
+ },
86
+ :help => ["profile,p USERNAME", "Show user's profile"]
64
87
  )
65
88
 
66
89
  register_command(
@@ -75,9 +98,13 @@ module Termtter::Client
75
98
  followers += tmp = Termtter::API.twitter.followers(user_name, :page => page+=1)
76
99
  end until tmp.empty?
77
100
  Termtter::Client.public_storage[:followers] = followers
101
+ public_storage[:users] += followers.map(&:screen_name)
78
102
  puts followers.map(&:screen_name).join(' ')
79
- }
80
- # TODO :completion_proc
103
+ },
104
+ :completion_proc => lambda {|cmd, arg|
105
+ find_user_candidates arg, "#{cmd} %s"
106
+ },
107
+ :help => ["followers", "Show followers"]
81
108
  )
82
109
 
83
110
  register_command(
@@ -94,22 +121,30 @@ module Termtter::Client
94
121
  },
95
122
  :completion_proc => lambda {|cmd, arg|
96
123
  find_user_candidates arg, "#{cmd} %s"
97
- }
124
+ },
125
+ :help => ["list,l [USERNAME]", "List the posts"]
98
126
  )
99
127
 
128
+ public_storage[:search_keywords] = Set.new
100
129
  register_command(
101
130
  :name => :search, :aliases => [:s],
102
131
  :exec_proc => lambda {|arg|
103
132
  statuses = Termtter::API.twitter.search(arg)
133
+ public_storage[:search_keywords] << arg
104
134
  output(statuses, :search)
105
- }
135
+ },
136
+ :completion_proc => lambda {|cmd, arg|
137
+ public_storage[:search_keywords].grep(/#{Regexp.quote(arg)}/).map { |i| "#{cmd} #{i}" }
138
+ },
139
+ :help => ["search,s TEXT", "Search for Twitter"]
106
140
  )
107
141
 
108
142
  register_command(
109
143
  :name => :replies, :aliases => [:r],
110
144
  :exec_proc => lambda {|arg|
111
145
  output(Termtter::API.twitter.replies, :replies)
112
- }
146
+ },
147
+ :help => ["replies,r", "List the most recent @replies for the authenticating user"]
113
148
  )
114
149
 
115
150
  register_command(
@@ -130,7 +165,8 @@ module Termtter::Client
130
165
  find_status_ids(arg).map {|id| "#{cmd} #{id}"}
131
166
  end
132
167
  end
133
- }
168
+ },
169
+ :help => ["show ID", "Show a single status"]
134
170
  )
135
171
 
136
172
  register_command(
@@ -138,7 +174,8 @@ module Termtter::Client
138
174
  :exec_proc => lambda {|arg|
139
175
  id = arg.gsub(/.*:\s*/, '')
140
176
  # TODO: Implement
141
- output([Termtter::API.twitter.show(id)], :show)
177
+ #output([Termtter::API.twitter.show(id)], :show)
178
+ puts "Not implemented yet."
142
179
  },
143
180
  :completion_proc => get_command(:show).completion_proc
144
181
  )
@@ -173,6 +210,61 @@ module Termtter::Client
173
210
  :help => ['leave USER', 'Leave user']
174
211
  )
175
212
 
213
+ register_command(
214
+ :name => :favorite, :aliases => [:fav],
215
+ :exec_proc => lambda {|arg|
216
+ id = 0
217
+ case arg
218
+ when /^\d+/
219
+ id = arg.to_i
220
+ when /^@([A-Za-z0-9_]+)/
221
+ user = $1
222
+ statuses = Termtter::API.twitter.user_timeline(user)
223
+ return if statuses.empty?
224
+ id = statuses[0].id
225
+ when /^\/(.*)$/
226
+ word = $1
227
+ raise "Not implemented yet."
228
+ else
229
+ return
230
+ end
231
+
232
+ r = Termtter::API.twitter.favorite id
233
+ puts "Favorited status ##{r.id} on user @#{r.user.screen_name} #{r.text}"
234
+ },
235
+ :completion_proc => lambda {|cmd, arg|
236
+ case arg
237
+ when /@(.*)/
238
+ find_user_candidates $1, "#{cmd} @%s"
239
+ when /(\d+)/
240
+ find_status_ids(arg).map{|id| "#{cmd} #{id}"}
241
+ else
242
+ %w(favorite).grep(/^#{Regexp.quote arg}/)
243
+ end
244
+ },
245
+ :help => ['favorite,fav (ID|@USER|/WORD)', 'Favorite a status']
246
+ )
247
+
248
+ def self.show_settings(conf, level = 0)
249
+ conf.__values__.each do |k, v|
250
+ if v.instance_of? Termtter::Config
251
+ puts "#{k}:"
252
+ show_settings v, level + 1
253
+ else
254
+ print ' ' * level
255
+ puts "#{k} = #{v.nil? ? 'nil' : v.inspect}"
256
+ end
257
+ end
258
+ end
259
+
260
+ register_command(
261
+ :name => :settings, :aliases => [:set],
262
+ :exec_proc => lambda {|_|
263
+ show_settings config
264
+ },
265
+ :help => ['settings,set', 'Show your settings']
266
+ )
267
+
176
268
  # TODO: Change colors when remaining_hits is low.
177
269
  # TODO: Simmulate remaining_hits.
178
270
  register_command(
@@ -210,61 +302,23 @@ module Termtter::Client
210
302
  :help => ['exit,e', 'Exit']
211
303
  )
212
304
 
213
- register_hook(
214
- :name => :default_error_handler,
215
- :points => [:on_error],
216
- :exec_proc => lambda {|e|
217
- case e
218
- when Rubytter::APIError
219
- case e.response.code
220
- when /401/
221
- warn '[ERROR] Unauthorized: maybe you tried to show protected user status'
222
- when /403/
223
- warn '[ERROR] Access denied: maybe that user is protected'
224
- when /404/
225
- warn '[ERROR] Not found: maybe there is no such user'
226
- end
227
- else
228
- warn "[ERROR] Something wrong: #{e.message}"
229
- end
230
- raise e if config.system.devel == true
231
- }
232
- )
233
-
234
305
  register_command(
235
306
  :name => :help, :aliases => [:h],
236
307
  :exec_proc => lambda {|arg|
237
- # TODO: move to each commands
238
- helps = [
239
- ["help,h", "Print this help message"],
240
- ["list,l", "List the posts in your friends timeline"],
241
- ["list,l USERNAME", "List the posts in the the given user's timeline"],
242
- ["update,u TEXT", "Post a new message"],
243
- ["direct,d USERNAME TEXT", "Send direct message"],
244
- ["profile,p USERNAME", "Show user's profile"],
245
- ["replies,r", "List the most recent @replies for the authenticating user"],
246
- ["search,s TEXT", "Search for Twitter"],
247
- ["show ID", "Show a single status"]
248
- ]
249
- helps += @commands.map {|name, command| command.help}
250
- helps.compact!
251
- puts formatted_help(helps)
252
- }
253
- )
254
-
255
- register_command(
256
- :name => :execute,
257
- :exec_proc => lambda{|arg|
258
- if arg
259
- `#{arg}`.each_line do |line|
260
- unless line.strip.empty?
261
- Termtter::API.twitter.update(line)
262
- puts "=> #{line}"
263
- end
308
+ helps = []
309
+ @commands.map do |name, command|
310
+ next unless command.help
311
+ case command.help[0]
312
+ when String
313
+ helps << command.help
314
+ when Array
315
+ helps += command.help
264
316
  end
265
317
  end
318
+ helps.compact!
319
+ puts formatted_help(helps)
266
320
  },
267
- :help => ['execute COMMAND', 'execute the command']
321
+ :help => ["help,h", "Print this help message"]
268
322
  )
269
323
 
270
324
  def self.formatted_help(helps)
@@ -278,9 +332,9 @@ module Termtter::Client
278
332
 
279
333
  # completion for standard commands
280
334
 
281
- require 'set'
282
335
  public_storage[:users] ||= Set.new
283
336
  public_storage[:status_ids] ||= Set.new
337
+ public_storage[:last_status_id] ||= Hash.new
284
338
 
285
339
  register_hook(
286
340
  :name => :for_completion,
@@ -291,24 +345,180 @@ module Termtter::Client
291
345
  public_storage[:users] += s.text.scan(/@([a-zA-Z_0-9]*)/).flatten
292
346
  public_storage[:status_ids].add(s.id)
293
347
  public_storage[:status_ids].add(s.in_reply_to_status_id) if s.in_reply_to_status_id
348
+ public_storage[:last_status_id].store(s.user.screen_name, s.id)
349
+ end
350
+ }
351
+ )
352
+
353
+ public_storage[:plugins] = (Dir["#{File.dirname(__FILE__)}/*.rb"] + Dir["#{Termtter::CONF_DIR}/plugins/*.rb"]).map do |f|
354
+ f.match(%r|([^/]+).rb$|)[1]
355
+ end
356
+
357
+ register_command(
358
+ :name => :plugin,
359
+ :exec_proc => lambda {|arg|
360
+ if arg.empty?
361
+ puts 'Should specify plugin name.'
362
+ return
363
+ end
364
+ begin
365
+ result = plugin arg
366
+ rescue LoadError
367
+ ensure
368
+ puts "=> #{result.inspect}"
369
+ end
370
+ },
371
+ :completion_proc => lambda {|cmd, args|
372
+ find_user_candidates args, "#{cmd} %s"
373
+ unless args.empty?
374
+ find_plugin_candidates args, "#{cmd} %s"
375
+ else
376
+ public_storage[:plugins].sort
377
+ end
378
+ },
379
+ :help => ['plugin FILE', 'Load a plugin']
380
+ )
381
+
382
+ register_command(
383
+ :name => :plugins,
384
+ :exec_proc => lambda {|arg|
385
+ puts public_storage[:plugins].sort.join("\n")
386
+ },
387
+ :help => ['plugins', 'Show list of plugins']
388
+ )
389
+
390
+ register_command(
391
+ :name => :reply,
392
+ :aliases => [:re],
393
+ :exec_proc => lambda {|arg|
394
+ case arg
395
+ when /^\s*(?:list|ls)\s*(?:\s+(\w+))?\s*$/
396
+ public_storage[:log4re] = if $1
397
+ public_storage[:log].
398
+ select{|l| l.user.screen_name == $1}.
399
+ sort {|a,b| a.id <=> b.id}
400
+ else
401
+ public_storage[:log].sort {|a,b| a.id <=> b.id}
402
+ end
403
+ public_storage[:log4re].each_with_index do |s, i|
404
+ puts "#{i}: #{s.user.screen_name}: #{s.text}"
405
+ end
406
+ when /^\s*(?:up(?:date)?)\s+(\d+)\s+(.+)$/
407
+ id = public_storage[:log4re][$1.to_i].id
408
+ text = $2
409
+ user = public_storage[:log4re][$1.to_i].user
410
+ update_with_user_and_id(text, user.screen_name, id) if user
411
+ public_storage.delete :log4re
412
+ when /^\s*(\d+)\s+(.+)$/
413
+ id, text = $1, $2
414
+ user = public_storage[:log].select {|l| l.id == id.to_i }.first.user
415
+ update_with_user_and_id(text, user.screen_name, id) if user
416
+ when /^\s*@(\w+)/
417
+ in_reply_to_status_id = Termtter::API.twitter.user($1).status.id rescue nil
418
+ params = in_reply_to_status_id ? {:in_reply_to_status_id => in_reply_to_status_id} : {}
419
+ Termtter::API.twitter.update(arg, params)
420
+ puts "=> #{arg}"
421
+ end
422
+ },
423
+ :completion_proc => lambda {|cmd, arg|
424
+ case arg
425
+ when /(.*)@([^\s]*)$/
426
+ find_user_candidates $2, "#{cmd} #{$1}@%s"
427
+ when /(\d+)/
428
+ find_status_ids(arg).map{|id| "#{cmd} #{id}"}
294
429
  end
295
430
  }
296
431
  )
297
432
 
433
+ register_command(
434
+ :name => :redo,
435
+ :aliases => [:"."],
436
+ :exec_proc => lambda {|arg|
437
+ break if Readline::HISTORY.length < 2
438
+ i = Readline::HISTORY.length - 2
439
+ input = ""
440
+ begin
441
+ input = Readline::HISTORY[i]
442
+ i -= 1
443
+ return if i <= 0
444
+ end while input == "redo" or input == "."
445
+ begin
446
+ Termtter::Client.call_commands(input)
447
+ rescue CommandNotFound => e
448
+ warn "Unknown command \"#{e}\""
449
+ warn 'Enter "help" for instructions'
450
+ rescue => e
451
+ handle_error e
452
+ end
453
+ },
454
+ :help => ["redo,.", "Execute previous command"]
455
+ )
456
+
457
+ def self.update_with_user_and_id(text, username, id)
458
+ text = ERB.new("@#{username} #{text}").result(binding).gsub(/\n/, ' ')
459
+ result = Termtter::API.twitter.update(text, {'in_reply_to_status_id' => id})
460
+ puts "=> #{text}"
461
+ result
462
+ end
463
+
464
+ =begin
465
+ = Termtter reply command
466
+ == Usage
467
+ === list
468
+ * ステータスリストを連番と一緒に出す。
469
+ > reply [list|ls]
470
+ 0: foo: foo's message
471
+ 1: bar: bar's message
472
+ ..
473
+
474
+ * ユーザ指定してリスト作成。
475
+ > reply [list|ls] foo
476
+ 0: foo: foo's message0
477
+ 1: foo: foo's message1
478
+
479
+ === reply
480
+ メッセージ送信の際、@usernameが自動的に付与される。
481
+
482
+ * status_idを自分で入力してreply送信
483
+ > reply 1234567890 message to status_id
484
+ => @foo message to status_id (reply to 1234567890)
485
+
486
+ * reply listコマンドで出したステータス番号に対してreply送信
487
+ > reply up 0 message to status no
488
+ => @foo message to status_no
489
+
490
+ * 対象ユーザの最後の発言に対してreply
491
+ > reply @foo message to foo
492
+ => @foo message to foo
493
+
494
+ == Todo
495
+ * 英語で説明 => ヘルプを設定する
496
+ * リファクタ
497
+ * 補完
498
+ * 確認画面を出したい
499
+ =end
500
+
501
+ def self.find_plugin_candidates(a, b)
502
+ public_storage[:plugins].
503
+ grep(/^#{Regexp.quote a}/i).
504
+ map {|u| b % u }
505
+ end
506
+
298
507
  def self.find_status_ids(text)
299
508
  public_storage[:status_ids].select {|id| /#{Regexp.quote(text)}/ =~ id.to_s}
300
509
  end
301
510
 
302
511
  def self.find_users(text)
303
- public_storage[:users].select {|user| /#{Regexp.quote(text)}/ =~ user}
512
+ public_storage[:users].select {|user| /^#{Regexp.quote(text)}/ =~ user}
304
513
  end
305
514
 
306
515
  def self.find_user_candidates(a, b)
307
- if a.nil? || a.empty?
308
- public_storage[:users].to_a
309
- else
310
- public_storage[:users].grep(/^#{Regexp.quote a}/i)
311
- end.
312
- map {|u| b % u }
516
+ users =
517
+ if a.nil? || a.empty?
518
+ public_storage[:users].to_a
519
+ else
520
+ public_storage[:users].grep(/^#{Regexp.quote a}/i)
521
+ end
522
+ users.map {|u| b % u }
313
523
  end
314
524
  end
@@ -46,11 +46,17 @@ module Termtter
46
46
  status_color = config.plugins.stdout.colors[s.user.id.hash % config.plugins.stdout.colors.size]
47
47
  status = "#{s.user.screen_name}: #{TermColor.escape(text)}"
48
48
  if s.in_reply_to_status_id
49
- status += " (repl. to #{s.in_reply_to_status_id})"
49
+ status += " (reply to #{s.in_reply_to_status_id})"
50
50
  end
51
51
 
52
52
  time = "(#{Time.parse(s.created_at).strftime(time_format)})"
53
53
  id = s.id
54
+ source =
55
+ case s.source
56
+ when />(.*?)</ then $1
57
+ when 'web' then 'web'
58
+ end
59
+
54
60
  erbed_text = ERB.new(config.plugins.stdout.timeline_format).result(binding)
55
61
  output_text << TermColor.parse(erbed_text) + "\n"
56
62
  end
@@ -0,0 +1,22 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Termtter::Client
4
+ register_command(
5
+ :name => :switch_user,
6
+ :exec_proc => lambda {|arg|
7
+ Termtter::API.switch_user(arg)
8
+ },
9
+ :completion_proc => lambda {|cmd, arg|
10
+ # TODO
11
+ },
12
+ :help => ["switch_user USERNAME", "Switch twitter account."]
13
+ )
14
+
15
+ register_command(
16
+ :name => :restore_user,
17
+ :exec_proc => lambda {|arg|
18
+ Termtter::API.restore_user
19
+ },
20
+ :help => ["restore_user", "Restore default twitter account."]
21
+ )
22
+ end
@@ -0,0 +1,20 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ Termtter::Client.register_hook(
4
+ :name => :tinyurl,
5
+ :points => [:modify_arg_for_update],
6
+ :exec_proc => lambda {|cmd, arg|
7
+ arg = arg.gsub(URI.regexp) do |url|
8
+ if url =~ %r!^https?:!
9
+ Termtter::API.connection.start('tinyurl.com', 80) do |http|
10
+ http.get('/api-create.php?url=' + URI.escape(url)).body
11
+ end
12
+ else
13
+ url
14
+ end
15
+ end
16
+ }
17
+ )
18
+
19
+ # tinyuri.rb
20
+ # make URLs in your update to convert tinyurl.com/XXXXXXX.
@@ -0,0 +1,94 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ config.plugins.typable_id.set_default(:typable_keys, %w[ a i u e o
4
+ ka ki ku ke ko
5
+ ga gi gu ge go
6
+ sa si su se so
7
+ za zi zu ze zo
8
+ ta ti tu te to
9
+ da di du de do
10
+ na ni nu ne no
11
+ ha hi hu he ho
12
+ ba bi bu be bo
13
+ pa pi pu pe po
14
+ ma mi mu me mo
15
+ ya yu yo
16
+ ra ri ru re ro
17
+ wa wo
18
+ nn ])
19
+
20
+ module Termtter::Client
21
+ public_storage[:typable_id] = []
22
+ config.plugins.typable_id.typable_keys.each {|key|
23
+ public_storage[:typable_id].push([key, '', ''])
24
+ }
25
+
26
+ register_hook(
27
+ :name => :typable_id,
28
+ :points => [:post_filter],
29
+ :exec_proc => lambda {|filtered, event|
30
+ filtered.each do |s|
31
+ current_id = public_storage[:typable_id].shift
32
+ current_id[1] = s.id.to_s
33
+ current_id[2] = s
34
+ public_storage[:typable_id].push(current_id)
35
+ end
36
+ }
37
+ )
38
+
39
+ def self.typable_id_convert(id)
40
+ if current_id = public_storage[:typable_id].assoc(id.to_s)
41
+ return current_id[1]
42
+ elsif current_id = public_storage[:typable_id].rassoc(id.to_s)
43
+ return current_id[0]
44
+ else
45
+ return nil
46
+ end
47
+ end
48
+
49
+ def self.typable_id_status(id)
50
+ if current_id = (public_storage[:typable_id].assoc(id.to_s)||\
51
+ public_storage[:typable_id].rassoc(id.to_s))
52
+ return current_id[2]
53
+ else
54
+ return nil
55
+ end
56
+ end
57
+
58
+ def self.typable_id?(id)
59
+ if public_storage[:typable_id].assoc(id.to_s)
60
+ return true
61
+ else
62
+ return false
63
+ end
64
+ end
65
+ end
66
+
67
+ #Setting
68
+ # ex)
69
+ # config.plugins.stdout.timeline_format = '<90><%=time%></90> <<%=status_color%>><%=status%></<%=status_color%>> <90><%=id%>>[<%=Termtter::Client.typable_id_convert(id)%>]</90>
70
+ #Optional Settng
71
+ # ex) Like hepburn system
72
+ # config.plugins.typable_id.typable_keys = %w[ a i u e o
73
+ # ka ki ku ke ko ga gi gu ge go
74
+ # sa shi su se so za ji zu ze zo
75
+ # ta chi tsu te to da di du de do
76
+ # na ni nu ne no
77
+ # ha hi fu he ho ba bi bu be bo pa pi pu pe po
78
+ # ma mi mu me mo
79
+ # ya yu yo
80
+ # ra ri ru re ro
81
+ # wa wo
82
+ # nn ]
83
+ # ex) Hiragana
84
+ # config.plugins.typable_id.typable_keys = %w[あ い う え お
85
+ # か き く け こ が ぎ ぐ げ ご
86
+ # さ し す せ そ ざ じ ず ぜ ぞ
87
+ # た ち つ て と だ ぢ づ で ど
88
+ # な に ぬ ね の
89
+ # は ひ ふ へ ほ ば び ぶ べ ぼ ぱ ぴ ぷ ぺ ぽ
90
+ # ま み む め も
91
+ # や ゆ よ
92
+ # ら り る れ ろ
93
+ # わ を
94
+ # ん ]